+ const char *p;
+ const char *astring;
+ char u[40];
+ ZebraService zs;
+
+ ZEBRA_CHECK_HANDLE(zh);
+
+ zs = zh->service;
+
+ sprintf(u, "perm.%.30s", user ? user : "anonymous");
+ p = res_get(zs->global_res, u);
+ xfree(zh->user_perm);
+ zh->user_perm = xstrdup(p ? p : "r");
+
+ /* Determine database access list */
+ astring = res_get(zs->dbaccess, user ? user : "anonymous");
+ if (astring)
+ zh->dbaccesslist = xstrdup(astring);
+ else
+ zh->dbaccesslist = 0;
+
+ /* users that don't require a password .. */
+ if (zh->user_perm && strchr(zh->user_perm, 'a'))
+ return ZEBRA_OK;
+
+ if (!zs->passwd_db || !passwd_db_auth(zs->passwd_db, user, pass))
+ return ZEBRA_OK;
+ return ZEBRA_FAIL;
+}
+
+ZEBRA_RES zebra_admin_import_begin(ZebraHandle zh, const char *database,
+ const char *record_type)
+{
+ yaz_log(log_level, "zebra_admin_import_begin db=%s rt=%s",
+ database, record_type);
+ if (zebra_select_database(zh, database) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ return zebra_begin_trans(zh, 1);
+}
+
+ZEBRA_RES zebra_admin_import_end(ZebraHandle zh)
+{
+ ZEBRA_CHECK_HANDLE(zh);
+ yaz_log(log_level, "zebra_admin_import_end");
+ return zebra_end_trans(zh);
+}
+
+ZEBRA_RES zebra_admin_import_segment(ZebraHandle zh, Z_Segment *segment)
+{
+ ZEBRA_RES res = ZEBRA_OK;
+ zint sysno;
+ int i;
+ ZEBRA_CHECK_HANDLE(zh);
+ yaz_log(log_level, "zebra_admin_import_segment");
+
+ for (i = 0; i<segment->num_segmentRecords; i++)
+ {
+ Z_NamePlusRecord *npr = segment->segmentRecords[i];
+
+ if (npr->which == Z_NamePlusRecord_intermediateFragment)
+ {
+ Z_FragmentSyntax *fragment = npr->u.intermediateFragment;
+ if (fragment->which == Z_FragmentSyntax_notExternallyTagged)
+ {
+ Odr_oct *oct = fragment->u.notExternallyTagged;
+ sysno = 0;
+
+ if(zebra_update_record(
+ zh,
+ action_update,
+ 0, /* record Type */
+ &sysno,
+ 0, /* match */
+ 0, /* fname */
+ (const char *) oct->buf, oct->len) == ZEBRA_FAIL)
+ res = ZEBRA_FAIL;
+ }
+ }
+ }
+ return res;
+}
+
+int delete_w_handle(const char *info, void *handle)
+{
+ ZebraHandle zh = (ZebraHandle) handle;
+ ISAM_P pos;
+
+ if (*info == sizeof(pos))
+ {
+ memcpy(&pos, info+1, sizeof(pos));
+ isamb_unlink(zh->reg->isamb, pos);
+ }
+ return 0;
+}
+
+int delete_w_all_handle(const char *info, void *handle)
+{
+ ZebraHandle zh = (ZebraHandle) handle;
+ ISAM_P pos;
+
+ if (*info == sizeof(pos))
+ {
+ ISAMB_PP pt;
+ memcpy(&pos, info+1, sizeof(pos));
+ pt = isamb_pp_open(zh->reg->isamb, pos, 2);
+ if (pt)
+ {
+ struct it_key key;
+ key.mem[0] = 0;
+ while (isamb_pp_read(pt, &key))
+ {
+ Record rec;
+ rec = rec_get(zh->reg->records, key.mem[0]);
+ rec_del(zh->reg->records, &rec);
+ }
+ isamb_pp_close(pt);
+ }
+ }
+ return delete_w_handle(info, handle);
+}
+
+static int delete_SU_handle(void *handle, int ord,
+ const char *index_type, const char *string_index,
+ zinfo_index_category_t cat)
+{
+ ZebraHandle zh = (ZebraHandle) handle;
+ char ord_buf[20];
+ int ord_len;
+#if 0
+ yaz_log(YLOG_LOG, "ord=%d index_type=%s index=%s cat=%d", ord,
+ index_type, string_index, (int) cat);
+#endif
+ ord_len = key_SU_encode(ord, ord_buf);
+ ord_buf[ord_len] = '\0';
+
+ assert(zh->reg->isamb);
+ assert(zh->reg->records);
+ dict_delete_subtree(zh->reg->dict, ord_buf,
+ zh,
+ !strcmp(string_index, "_ALLRECORDS") ?
+ delete_w_all_handle : delete_w_handle);
+ return 0;
+}
+
+ZEBRA_RES zebra_drop_database(ZebraHandle zh, const char *db)
+{
+ ZEBRA_RES ret = ZEBRA_OK;
+
+ yaz_log(log_level, "zebra_drop_database %s", db);
+ ZEBRA_CHECK_HANDLE(zh);
+
+ if (zebra_select_database(zh, db) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ if (zebra_begin_trans(zh, 1) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ if (zh->reg->isamb)
+ {
+ int db_ord;
+ if (zebraExplain_curDatabase(zh->reg->zei, db))
+ {
+ zebra_setError(zh, YAZ_BIB1_DATABASE_DOES_NOT_EXIST, db);
+ ret = ZEBRA_FAIL;
+ }
+ else
+ {
+ db_ord = zebraExplain_get_database_ord(zh->reg->zei);
+ dict_delete_subtree_ord(zh->reg->matchDict, db_ord,
+ 0 /* handle */, 0 /* func */);
+ zebraExplain_trav_ord(zh->reg->zei, zh, delete_SU_handle);
+ zebraExplain_removeDatabase(zh->reg->zei, zh);
+ zebra_remove_file_match(zh);
+ }
+ }
+ else
+ {
+ yaz_log(YLOG_WARN, "drop database only supported for isam:b");
+ zebra_setError(zh, YAZ_BIB1_ES_IMMEDIATE_EXECUTION_FAILED,
+ "drop database only supported for isam:b");
+ ret = ZEBRA_FAIL;
+ }
+ if (zebra_end_trans(zh) != ZEBRA_OK)
+ {
+ yaz_log(YLOG_WARN, "zebra_end_trans failed");
+ ret = ZEBRA_FAIL;
+ }
+ return ret;
+}
+
+ZEBRA_RES zebra_create_database(ZebraHandle zh, const char *db)
+{
+ yaz_log(log_level, "zebra_create_database %s", db);
+ ZEBRA_CHECK_HANDLE(zh);
+ assert(db);
+
+ if (zebra_select_database(zh, db) == ZEBRA_FAIL)
+ return ZEBRA_FAIL;
+ if (zebra_begin_trans(zh, 1))
+ return ZEBRA_FAIL;
+
+ /* announce database */
+ if (zebraExplain_newDatabase(zh->reg->zei, db, 0
+ /* explainDatabase */))
+ {
+ if (zebra_end_trans(zh) != ZEBRA_OK)
+ {
+ yaz_log(YLOG_WARN, "zebra_end_trans failed");
+ }
+ zebra_setError(zh, YAZ_BIB1_ES_IMMEDIATE_EXECUTION_FAILED, db);
+ return ZEBRA_FAIL;
+ }
+ return zebra_end_trans(zh);
+}
+
+int zebra_string_norm(ZebraHandle zh, const char *index_type,
+ const char *input_str, int input_len,
+ char *output_str, int output_len)
+{
+ WRBUF wrbuf;
+ zebra_map_t zm = zebra_map_get(zh->reg->zebra_maps, index_type);
+ ASSERTZH;
+ assert(input_str);
+ assert(output_str);
+ yaz_log(log_level, "zebra_string_norm ");
+
+ if (!zh->reg->zebra_maps)
+ return -1;
+ wrbuf = zebra_replace(zm, "", input_str, input_len);
+ if (!wrbuf)
+ return -2;
+ if (wrbuf_len(wrbuf) >= output_len)
+ return -3;
+ if (wrbuf_len(wrbuf))
+ memcpy(output_str, wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
+ output_str[wrbuf_len(wrbuf)] = '\0';
+ return wrbuf_len(wrbuf);
+}
+
+/** \brief set register state (state*.LCK)
+ \param zh Zebra handle
+ \param val state
+ \param seqno sequence number
+
+ val is one of:
+ d=writing to shadow(shadow enabled); writing to register (shadow disabled)
+ o=reading only
+ c=commit (writing to register, reading from shadow, shadow mode only)
+*/
+static void zebra_set_state(ZebraHandle zh, int val, int seqno)
+{
+ char state_fname[256];
+ char *fname;
+ long p = getpid();
+ FILE *f;
+ ASSERTZH;
+ yaz_log(log_level, "zebra_set_state v=%c seq=%d", val, seqno);
+
+ sprintf(state_fname, "state.%s.LCK", zh->reg_name);
+ fname = zebra_mk_fname(res_get(zh->res, "lockDir"), state_fname);
+ f = fopen(fname, "w");
+ if (!f)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "open %s w", state_fname);
+ exit(1);
+ }
+ yaz_log(YLOG_DEBUG, "zebra_set_state: %c %d %ld", val, seqno, p);
+ fprintf(f, "%c %d %ld\n", val, seqno, p);
+ fclose(f);
+ xfree(fname);
+}
+
+static void zebra_get_state(ZebraHandle zh, char *val, int *seqno)
+{
+ char state_fname[256];
+ char *fname;
+ FILE *f;
+
+ ASSERTZH;
+ yaz_log(log_level, "zebra_get_state ");
+
+ sprintf(state_fname, "state.%s.LCK", zh->reg_name);
+ fname = zebra_mk_fname(res_get(zh->res, "lockDir"), state_fname);
+ f = fopen(fname, "r");
+ *val = 'o';
+ *seqno = 0;
+
+ if (f)
+ {
+ if (fscanf(f, "%c %d", val, seqno) != 2)
+ {
+ yaz_log(YLOG_ERRNO|YLOG_WARN, "fscan fail %s",
+ state_fname);
+ }
+ fclose(f);
+ }
+ xfree(fname);
+}
+
+ZEBRA_RES zebra_begin_read(ZebraHandle zh)
+{
+ return zebra_begin_trans(zh, 0);
+}
+
+ZEBRA_RES zebra_end_read(ZebraHandle zh)
+{
+ return zebra_end_trans(zh);
+}
+
+static void read_res_for_transaction(ZebraHandle zh)
+{
+ const char *group = res_get(zh->res, "group");
+ const char *v;
+ /* FIXME - do we still use groups ?? */
+
+ zh->m_group = group;
+ v = res_get_prefix(zh->res, "followLinks", group, "1");
+ zh->m_follow_links = atoi(v);
+
+ zh->m_record_id = res_get_prefix(zh->res, "recordId", group, 0);
+ zh->m_record_type = res_get_prefix(zh->res, "recordType", group, 0);
+
+ v = res_get_prefix(zh->res, "storeKeys", group, "1");
+ zh->m_store_keys = atoi(v);
+
+ v = res_get_prefix(zh->res, "storeData", group, "1");
+ zh->m_store_data = atoi(v);
+
+ v = res_get_prefix(zh->res, "explainDatabase", group, "0");
+ zh->m_explain_database = atoi(v);
+
+ v = res_get_prefix(zh->res, "openRW", group, "1");
+ zh->m_flag_rw = atoi(v);
+
+ v = res_get_prefix(zh->res, "fileVerboseLimit", group, "1000");
+ zh->m_file_verbose_limit = atoi(v);
+}
+
+ZEBRA_RES zebra_begin_trans(ZebraHandle zh, int rw)
+{
+ ZEBRA_CHECK_HANDLE(zh);
+ zebra_select_default_database(zh);
+ if (!zh->res)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: no database selected");
+ return ZEBRA_FAIL;
+ }
+ ASSERTZHRES;
+ yaz_log(log_level, "zebra_begin_trans rw=%d",rw);
+
+ if (zh->user_perm)
+ {
+ if (rw && !strchr(zh->user_perm, 'w'))
+ {
+ zebra_setError(
+ zh,
+ YAZ_BIB1_ES_PERMISSION_DENIED_ON_ES_CANNOT_MODIFY_OR_DELETE,
+ 0);
+ return ZEBRA_FAIL;
+ }
+ }
+
+ assert(zh->res);
+ if (rw)
+ {
+ int seqno = 0;
+ char val = '?';
+ const char *rval = 0;
+
+ (zh->trans_no++);
+ if (zh->trans_w_no)
+ {
+ read_res_for_transaction(zh);
+ return 0;
+ }
+ if (zh->trans_no != 1)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: no write trans within read");
+ return ZEBRA_FAIL;
+ }
+ if (zh->reg)
+ {
+ resultSetInvalidate(zh);
+ zebra_register_close(zh->service, zh->reg);
+ }
+ zh->trans_w_no = zh->trans_no;
+
+ zh->records_inserted = 0;
+ zh->records_updated = 0;
+ zh->records_deleted = 0;
+ zh->records_processed = 0;
+ zh->records_skipped = 0;
+
+#if HAVE_SYS_TIMES_H
+ times(&zh->tms1);
+#endif
+ /* lock */
+ if (zh->shadow_enable)
+ rval = res_get(zh->res, "shadow");
+
+ if (rval)
+ {
+ zebra_lock_r(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ else
+ {
+ zebra_lock_w(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ zebra_get_state(zh, &val, &seqno);
+ if (val != 'o')
+ {
+ /* either we didn't finish commit or shadow is dirty */
+ if (!rval)
+ {
+ yaz_log(YLOG_WARN, "previous transaction did not finish "
+ "(shadow disabled)");
+ }
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+ if (zebra_commit(zh))
+ {
+ zh->trans_no--;
+ zh->trans_w_no = 0;
+ return ZEBRA_FAIL;
+ }
+ if (rval)
+ {
+ zebra_lock_r(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ else
+ {
+ zebra_lock_w(zh->lock_normal);
+ zebra_lock_w(zh->lock_shadow);
+ }
+ }
+
+ zebra_set_state(zh, 'd', seqno);
+
+ zh->reg = zebra_register_open(zh->service, zh->reg_name,
+ 1, rval ? 1 : 0, zh->res,
+ zh->path_reg);
+ if (!zh->reg)
+ {
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+
+ zh->trans_no--;
+ zh->trans_w_no = 0;
+
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_begin_trans: cannot open register");
+ yaz_log(YLOG_FATAL, "%s", zh->errString);
+ return ZEBRA_FAIL;
+ }
+ zh->reg->seqno = seqno;
+ zebraExplain_curDatabase(zh->reg->zei, zh->basenames[0]);
+ }
+ else
+ {
+ int dirty = 0;
+ char val;
+ int seqno;
+
+ (zh->trans_no)++;
+
+ if (zh->trans_no != 1)
+ {
+ return zebra_flush_reg(zh);
+ }
+#if HAVE_SYS_TIMES_H
+ times(&zh->tms1);
+#endif
+ if (!zh->res)
+ {
+ (zh->trans_no)--;
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ if (!zh->lock_normal || !zh->lock_shadow)
+ {
+ (zh->trans_no)--;
+ zh->errCode = YAZ_BIB1_TEMPORARY_SYSTEM_ERROR;
+ return ZEBRA_FAIL;
+ }
+ zebra_get_state(zh, &val, &seqno);
+ if (val == 'd')
+ val = 'o';
+
+ if (!zh->reg)
+ dirty = 1;
+ else if (seqno != zh->reg->seqno)
+ {
+ yaz_log(YLOG_DEBUG, "reopen seqno cur/old %d/%d",
+ seqno, zh->reg->seqno);
+ dirty = 1;
+ }
+ else if (zh->reg->last_val != val)
+ {
+ yaz_log(YLOG_DEBUG, "reopen last cur/old %d/%d",
+ val, zh->reg->last_val);
+ dirty = 1;
+ }
+ if (!dirty)
+ return ZEBRA_OK;
+
+ if (val == 'c')
+ zebra_lock_r(zh->lock_shadow);
+ else
+ zebra_lock_r(zh->lock_normal);
+
+ if (zh->reg)
+ {
+ resultSetInvalidate(zh);
+ zebra_register_close(zh->service, zh->reg);
+ }
+ zh->reg = zebra_register_open(zh->service, zh->reg_name,
+ 0, val == 'c' ? 1 : 0,
+ zh->res, zh->path_reg);
+ if (!zh->reg)
+ {
+ zebra_unlock(zh->lock_normal);
+ zebra_unlock(zh->lock_shadow);
+ zh->trans_no--;
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ zh->reg->last_val = val;
+ zh->reg->seqno = seqno;
+ }
+ read_res_for_transaction(zh);
+ return ZEBRA_OK;
+}
+
+ZEBRA_RES zebra_end_trans(ZebraHandle zh)
+{
+ ZebraTransactionStatus dummy;
+
+ yaz_log(log_level, "zebra_end_trans");
+ ZEBRA_CHECK_HANDLE(zh);
+ return zebra_end_transaction(zh, &dummy);
+}
+
+ZEBRA_RES zebra_end_transaction(ZebraHandle zh, ZebraTransactionStatus *status)
+{
+ char val;
+ int seqno;
+ const char *rval;
+
+ ZEBRA_CHECK_HANDLE(zh);
+
+ assert(status);
+ yaz_log(log_level, "zebra_end_transaction");
+
+ status->processed = 0;
+ status->inserted = 0;
+ status->updated = 0;
+ status->deleted = 0;
+ status->utime = 0;
+ status->stime = 0;
+
+ if (!zh->res || !zh->reg)
+ {
+ zebra_setError(zh, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
+ "zebra_end_trans: no open transaction");
+ return ZEBRA_FAIL;
+ }
+ if (zh->trans_no != zh->trans_w_no)
+ {
+ zh->trans_no--;
+ if (zh->trans_no != 0)
+ return ZEBRA_OK;
+
+ /* release read lock */
+
+ zebra_unlock(zh->lock_normal);
+ zebra_unlock(zh->lock_shadow);
+ }
+ else
+ { /* release write lock */
+ zh->trans_no--;
+ zh->trans_w_no = 0;
+
+ yaz_log(YLOG_DEBUG, "zebra_end_trans");
+ rval = res_get(zh->res, "shadow");
+
+ zebraExplain_runNumberIncrement(zh->reg->zei, 1);
+
+ zebra_flush_reg(zh);
+
+ resultSetInvalidate(zh);
+
+ zebra_register_close(zh->service, zh->reg);
+ zh->reg = 0;
+
+ yaz_log(YLOG_LOG, "Records: "ZINT_FORMAT" i/u/d "
+ ZINT_FORMAT"/"ZINT_FORMAT"/"ZINT_FORMAT,
+ zh->records_processed, zh->records_inserted,
+ zh->records_updated, zh->records_deleted);
+
+ status->processed = zh->records_processed;
+ status->inserted = zh->records_inserted;
+ status->updated = zh->records_updated;
+ status->deleted = zh->records_deleted;
+
+ zebra_get_state(zh, &val, &seqno);
+ if (val != 'd')
+ {
+ BFiles bfs = bfs_create(rval, zh->path_reg);
+ bf_commitClean(bfs, rval);
+ bfs_destroy(bfs);
+ }
+ if (!rval)
+ seqno++;
+ zebra_set_state(zh, 'o', seqno);
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+
+ }
+#if HAVE_SYS_TIMES_H
+ times(&zh->tms2);
+ yaz_log(log_level, "user/system: %ld/%ld",
+ (long) (zh->tms2.tms_utime - zh->tms1.tms_utime),
+ (long) (zh->tms2.tms_stime - zh->tms1.tms_stime));
+
+ status->utime = (long) (zh->tms2.tms_utime - zh->tms1.tms_utime);
+ status->stime = (long) (zh->tms2.tms_stime - zh->tms1.tms_stime);
+#endif
+ return ZEBRA_OK;
+}
+
+ZEBRA_RES zebra_repository_update(ZebraHandle zh, const char *path)
+{
+ return zebra_repository_index(zh, path, action_update);
+}
+
+ZEBRA_RES zebra_repository_delete(ZebraHandle zh, const char *path)
+{
+ return zebra_repository_index(zh, path, action_delete);
+}
+
+ZEBRA_RES zebra_repository_index(ZebraHandle zh, const char *path,
+ enum zebra_recctrl_action_t action)
+{
+ ASSERTZH;
+ assert(path);
+
+ if (action == action_update)
+ yaz_log(log_level, "updating %s", path);
+ else if (action == action_delete)
+ yaz_log(log_level, "deleting %s", path);
+ else if (action == action_a_delete)
+ yaz_log(log_level, "attempt deleting %s", path);
+ else
+ yaz_log(log_level, "update action=%d", (int) action);
+
+ if (zh->m_record_id && !strcmp(zh->m_record_id, "file"))
+ return zebra_update_file_match(zh, path);
+ else
+ return zebra_update_from_path(zh, path, action);
+}
+
+ZEBRA_RES zebra_repository_show(ZebraHandle zh, const char *path)
+{
+ ASSERTZH;
+ assert(path);
+ yaz_log(log_level, "zebra_repository_show");
+ repositoryShow(zh, path);
+ return ZEBRA_OK;
+}
+
+static ZEBRA_RES zebra_commit_ex(ZebraHandle zh, int clean_only)
+{
+ int seqno;
+ char val;
+ const char *rval;
+ BFiles bfs;
+ ZEBRA_RES res = ZEBRA_OK;
+
+ ASSERTZH;
+
+ yaz_log(log_level, "zebra_commit_ex clean_only=%d", clean_only);
+ zebra_select_default_database(zh);
+ if (!zh->res)
+ {
+ zh->errCode = YAZ_BIB1_DATABASE_UNAVAILABLE;
+ return ZEBRA_FAIL;
+ }
+ rval = res_get(zh->res, "shadow");
+ if (!rval)
+ {
+ yaz_log(YLOG_WARN, "Cannot perform commit - No shadow area defined");
+ return ZEBRA_OK;
+ }
+
+ zebra_lock_w(zh->lock_normal);
+ zebra_lock_r(zh->lock_shadow);
+
+ bfs = bfs_create(res_get(zh->res, "register"), zh->path_reg);
+ if (!bfs)
+ {
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+ return ZEBRA_FAIL;
+ }
+ zebra_get_state(zh, &val, &seqno);
+
+ if (val == 'd')
+ {
+ /* shadow area is dirty and so we must throw it away */
+ yaz_log(YLOG_WARN, "previous transaction didn't reach commit");
+ clean_only = 1;
+ }
+ else if (val == 'c')
+ {
+ /* commit has started. We can not remove it anymore */
+ clean_only = 0;
+ }
+
+ if (rval && *rval)
+ bf_cache(bfs, rval);
+ if (bf_commitExists(bfs))
+ {
+ if (clean_only)
+ zebra_set_state(zh, 'd', seqno);
+ else
+ {
+ zebra_set_state(zh, 'c', seqno);
+
+ yaz_log(log_level, "commit start");
+ if (bf_commitExec(bfs))
+ res = ZEBRA_FAIL;
+ }
+ if (res == ZEBRA_OK)
+ {
+ seqno++;
+ zebra_set_state(zh, 'o', seqno);
+
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+
+ zebra_lock_w(zh->lock_shadow);
+ bf_commitClean(bfs, rval);
+ zebra_unlock(zh->lock_shadow);
+ }
+ else
+ {
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+ yaz_log(YLOG_WARN, "zebra_commit: failed");
+ }
+ }
+ else
+ {
+ zebra_unlock(zh->lock_shadow);
+ zebra_unlock(zh->lock_normal);
+ yaz_log(log_level, "nothing to commit");
+ }
+ bfs_destroy(bfs);
+
+ return res;