virtual-storage.c revision ca0cfbcd16563416b37e3c3e9062349a9d1c9cf7
/* Copyright (c) 2008-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "str.h"
#include "llist.h"
#include "mkdir-parents.h"
#include "unlink-directory.h"
#include "index-mail.h"
#include "mail-copy.h"
#include "mail-search.h"
#include "mailbox-list-private.h"
#include "virtual-plugin.h"
#include "virtual-transaction.h"
#include "virtual-storage.h"
#include "mailbox-list-notify.h"
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#define VIRTUAL_DEFAULT_MAX_OPEN_MAILBOXES 64
#define VIRTUAL_BACKEND_CONTEXT(obj) \
struct virtual_backend_mailbox {
union mailbox_module_context module_ctx;
};
extern struct mail_storage virtual_storage;
extern struct mailbox virtual_mailbox;
extern struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs;
{
}
{
else {
return t_strdup_printf("<hidden>%c%s",
}
}
{
enum mail_error error;
}
static struct mail_storage *virtual_storage_alloc(void)
{
struct virtual_storage *storage;
}
static int
const char **error_r)
{
const char *value;
*error_r = "Invalid virtual_max_open_mailboxes setting";
return -1;
}
return 0;
}
static void
struct mailbox_list_settings *set)
{
}
struct virtual_backend_box *
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
for (i = 0; i < count; i++) {
return bboxes[i];
}
return NULL;
}
struct virtual_backend_box *
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
if (mailbox_id == 0)
return NULL;
for (i = 0; i < count; i++) {
return bboxes[i];
}
return NULL;
}
const char *name)
{
const char *const *names;
unsigned int i, count;
for (i = 0; i < count; i++) {
return TRUE;
}
return FALSE;
}
struct virtual_backend_box *bbox)
{
enum mail_error error;
const char *str;
"Virtual mailbox open failed because of mailbox %s: %s",
/* this mailbox wasn't explicitly specified. just skip it. */
return 0;
}
return -1;
}
struct virtual_backend_box *bbox,
enum mailbox_flags flags)
{
struct mail_namespace *ns;
const char *mailbox;
enum mailbox_existence existence;
if (!bbox->clear_recent)
/* Assume that the save_bbox exists, whether or not it truly
does. This at least gives a better error message than crash
later on. */
} else {
}
if (existence != MAILBOX_EXISTENCE_SELECT) {
/* ignore this. it could be intentional. */
i_debug("virtual mailbox %s: "
"Skipping non-existing mailbox %s",
}
return 0;
}
/* we use modseqs for being able to check quickly if backend mailboxes
have changed. make sure the backend has them enabled. */
return 1;
}
enum mailbox_flags flags)
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
int ret;
for (i = 0; i < count; ) {
if (ret <= 0) {
if (ret < 0)
break;
} else {
i++;
}
}
if (i == count)
return 0;
else {
/* failed */
for (; i > 0; i--) {
}
return -1;
}
}
static struct mailbox *
{
struct virtual_mailbox *mbox;
}
{
struct mailbox_transaction_context *trans;
(void)mailbox_transaction_commit(&trans);
}
}
{
/* we can close it if notify is set
because we have no need to keep it open
for tracking changes */
}
/* FIXME: we could probably close this by making
syncing support it? */
return FALSE;
}
return TRUE;
}
static bool
struct virtual_backend_box *except_bbox)
{
struct virtual_backend_box *bbox;
/* first try to close a mailbox without any transactions.
we'll also skip any mailbox that has notifications enabled (ideally
these would be handled by mailbox list index) */
if (bbox != except_bbox &&
return TRUE;
}
}
/* next try to close a mailbox that has sync_mail, but no
other transactions */
if (bbox != except_bbox &&
return TRUE;
}
}
return FALSE;
}
{
/* we could have gotten here from e.g. mailbox_autocreate()
without going through virtual_mailbox_close() */
}
}
{
struct virtual_backend_mailbox *vbox;
}
{
struct virtual_mailbox *mbox;
/* not a backend for a virtual mailbox */
return;
}
/* the backend mailbox was already opened. if we didn't get here
from virtual_backend_box_open() we may need to close a mailbox */
;
}
struct virtual_backend_box *bbox)
{
/* try to keep the number of open mailboxes below the threshold
before opening the mailbox */
;
}
struct virtual_backend_box *bbox)
{
}
/* stop receiving notifications */
if (bbox->notify_changes_started)
}
struct virtual_backend_box *bbox)
{
}
{
struct virtual_backend_box **bboxes;
unsigned int i, count;
for (i = 0; i < count; i++) {
continue;
}
}
static int
enum mailbox_existence *existence_r)
{
}
{
bool broken;
int ret = 0;
return -1;
}
if (ret == 0) {
}
if (ret < 0) {
return -1;
}
return -1;
sizeof(struct virtual_mail_index_record),
sizeof(uint32_t));
0, 0);
return -1;
}
/* if GUID is missing write it here */
struct mail_index_transaction *t =
if (mail_index_transaction_commit(&t) < 0) {
"Cannot write GUID for virtual mailbox %s to index",
return -1;
}
}
return 0;
}
{
}
{
}
static int
bool directory ATTR_UNUSED)
{
"Can't create virtual mailboxes");
return -1;
}
static int
{
"Can't update virtual mailboxes");
return -1;
}
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
struct mailbox_status status;
return -1;
}
for (i = 0; i < count; i++) {
const char *errstr;
enum mail_error error;
if (error == MAIL_ERROR_NOTFOUND) {
/* backend mailbox was just lost - skip it */
continue;
}
/* Not expected to happen, but we can't return failure
since this could be called from
mailbox_get_open_status() and it would panic.
So just log the error and skip the mailbox. */
"Virtual mailbox %s: Failed to get have_guid existence for backend mailbox %s: %s",
continue;
}
if (!status.have_guids)
if (!status.have_save_guids)
}
return 0;
}
static int
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
if ((items & STATUS_LAST_CACHED_SEQ) != 0)
items |= STATUS_MESSAGES;
return -1;
if ((items & STATUS_LAST_CACHED_SEQ) != 0) {
/* Virtual mailboxes have no cached data of their own, so the
current value is always 0. The most important use for this
functionality is for "doveadm index" to do FTS indexing and
it doesn't really matter there if we set this value
correctly or not. So for now just assume that everything is
indexed. */
}
if (!mbox->have_guid_flags_set) {
if (virtual_storage_set_have_guid_flags(mbox) < 0)
return -1;
}
if (mbox->have_guids)
if (mbox->have_save_guids)
return 0;
}
static int
enum mailbox_metadata_items items,
struct mailbox_metadata *metadata_r)
{
return -1;
if ((items & MAILBOX_METADATA_GUID) != 0) {
return -1;
}
}
return 0;
}
static void
{
}
{
}
{
/* did not support notifications */
return -1;
return 0;
}
{
struct virtual_backend_box **bboxp;
if ((*bboxp)->notify_changes_started) {
}
}
return;
}
/* There's only a single backend mailbox and its
indexes are already opened. Might as well use the
backend directly for notifications. */
} else {
/* we are already waiting for notifications */
continue;
/* wait for notifications */
if (virtual_notify_start(*bboxp) == 0)
continue;
/* it did not work, so open the mailbox and use
alternative method */
}
/* we can't report error in here, so do it later */
continue;
}
}
}
static void
struct mailbox *backend_mailbox,
{
struct virtual_backend_box *bbox;
const struct virtual_backend_uidmap *uids;
struct seq_range_iter iter;
unsigned int n, i, count;
else {
}
return;
uids[i].virtual_uid);
i++;
}
}
}
static void
struct mailbox *backend_mailbox,
{
struct virtual_backend_box *bbox;
const struct virtual_backend_uidmap *uids;
struct seq_range_iter iter;
unsigned int n, i, count;
else {
}
return;
} else {
i++;
}
}
}
static void
bool only_with_msgs)
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
for (i = 0; i < count; i++) {
}
}
{
if (mbox->inconsistent)
return TRUE;
return index_storage_is_inconsistent(box);
}
static int
{
/* we don't have any quick and easy optimizations for tracking
virtual folders. ideally we'd completely disable mailbox list
indexes for them, but this is the easiest way to do it for now. */
return 1;
}
static void
{
}
struct mail_storage virtual_storage = {
.v = {
NULL,
NULL,
NULL,
NULL,
NULL,
}
};
struct mailbox virtual_mailbox = {
.v = {
NULL,
NULL,
NULL,
NULL,
NULL,
}
};
struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs = {
};