notmuch/lib/config.cc
David Bremner 582e919e27 lib/config: don't set destructor until iterator is initialized.
As diagnosed by Olivier Taïbi in
id:20201027100916.emry3k2wujod4xnl@galois.lan, if an exception is
thrown while the initialization is happening (e.g. if the function is
called on a closed database), then the destructor is (sometimes)
invoked on an uninitialized Xapian object.

Solve the problem by moving the setting of the destructor until after
the placement new successfully completes. It is conceivable this might
cause a memory leak, but that seems preferable to crashing, and in any
case, there seems to be nothing better to be done if the
initialization is failing things are in an undefined state by
definition.
2020-10-29 21:13:01 -03:00

196 lines
4.9 KiB
C++

/* config.cc - API for database metadata
*
* Copyright © 2016 David Bremner
*
* 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 3 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, see https://www.gnu.org/licenses/ .
*
* Author: David Bremner <david@tethera.net>
*/
#include "notmuch.h"
#include "notmuch-private.h"
#include "database-private.h"
static const std::string CONFIG_PREFIX = "C";
struct _notmuch_config_list {
notmuch_database_t *notmuch;
Xapian::TermIterator iterator;
char *current_key;
char *current_val;
};
static int
_notmuch_config_list_destroy (notmuch_config_list_t *list)
{
/* invoke destructor w/o deallocating memory */
list->iterator.~TermIterator();
return 0;
}
notmuch_status_t
notmuch_database_set_config (notmuch_database_t *notmuch,
const char *key,
const char *value)
{
notmuch_status_t status;
status = _notmuch_database_ensure_writable (notmuch);
if (status)
return status;
try {
notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value);
} catch (const Xapian::Error &error) {
status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
notmuch->exception_reported = true;
_notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
error.get_msg ().c_str ());
}
return status;
}
static notmuch_status_t
_metadata_value (notmuch_database_t *notmuch,
const char *key,
std::string &value)
{
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
try {
value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key);
} catch (const Xapian::Error &error) {
status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
notmuch->exception_reported = true;
_notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
error.get_msg ().c_str ());
}
return status;
}
notmuch_status_t
notmuch_database_get_config (notmuch_database_t *notmuch,
const char *key,
char **value)
{
std::string strval;
notmuch_status_t status;
if (! value)
return NOTMUCH_STATUS_NULL_POINTER;
status = _metadata_value (notmuch, key, strval);
if (status)
return status;
*value = strdup (strval.c_str ());
return NOTMUCH_STATUS_SUCCESS;
}
notmuch_status_t
notmuch_database_get_config_list (notmuch_database_t *notmuch,
const char *prefix,
notmuch_config_list_t **out)
{
notmuch_config_list_t *list = NULL;
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
list = talloc (notmuch, notmuch_config_list_t);
if (! list) {
status = NOTMUCH_STATUS_OUT_OF_MEMORY;
goto DONE;
}
list->notmuch = notmuch;
list->current_key = NULL;
list->current_val = NULL;
try {
new(&(list->iterator)) Xapian::TermIterator (notmuch->xapian_db->metadata_keys_begin
(CONFIG_PREFIX + (prefix ? prefix : "")));
talloc_set_destructor (list, _notmuch_config_list_destroy);
} catch (const Xapian::Error &error) {
_notmuch_database_log (notmuch, "A Xapian exception occurred getting metadata iterator: %s.\n",
error.get_msg ().c_str ());
notmuch->exception_reported = true;
status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
}
*out = list;
DONE:
if (status && list)
talloc_free (list);
return status;
}
notmuch_bool_t
notmuch_config_list_valid (notmuch_config_list_t *metadata)
{
if (metadata->iterator == metadata->notmuch->xapian_db->metadata_keys_end ())
return false;
return true;
}
static inline char * _key_from_iterator (notmuch_config_list_t *list) {
return talloc_strdup (list, (*list->iterator).c_str () + CONFIG_PREFIX.length ());
}
const char *
notmuch_config_list_key (notmuch_config_list_t *list)
{
if (list->current_key)
talloc_free (list->current_key);
list->current_key = _key_from_iterator (list);
return list->current_key;
}
const char *
notmuch_config_list_value (notmuch_config_list_t *list)
{
std::string strval;
notmuch_status_t status;
char *key = _key_from_iterator (list);
/* TODO: better error reporting?? */
status = _metadata_value (list->notmuch, key, strval);
if (status)
return NULL;
if (list->current_val)
talloc_free (list->current_val);
list->current_val = talloc_strdup (list, strval.c_str ());
talloc_free (key);
return list->current_val;
}
void
notmuch_config_list_move_to_next (notmuch_config_list_t *list)
{
list->iterator++;
}
void
notmuch_config_list_destroy (notmuch_config_list_t *list)
{
talloc_free (list);
}