view src/doveadm/dsync/dsync-mailbox-tree.h @ 22715:20415dd0b85a

dsync: Add per-mailbox sync lock that is always used. Both importing and exporting gets the lock before they even sync the mailbox. The lock is kept until the import/export finishes. This guarantees that no matter how dsync is run, two dsyncs can't be working on the same mailbox at the same time. This lock is in addition to the optional per-user lock enabled by the -l parameter. If the -l parameter is used, the same lock timeout is used for the per-mailbox lock. Otherwise 30s timeout is used. This should help to avoid email duplication when replication is enabled for public namespaces, and maybe in some other rare situations as well.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 28 Dec 2017 14:10:23 +0200
parents 8d56903b8c57
children
line wrap: on
line source

#ifndef DSYNC_MAILBOX_TREE_H
#define DSYNC_MAILBOX_TREE_H

#include "guid.h"
#include "mail-error.h"

struct mail_namespace;
struct dsync_brain;

enum dsync_mailbox_trees_sync_type {
	/* two-way sync for both mailboxes */
	DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY,
	/* make remote tree look exactly like the local tree */
	DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL,
	/* make local tree look exactly like the remote tree */
	DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE
};

enum dsync_mailbox_trees_sync_flags {
	/* Enable debugging */
	DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG		= 0x01,
	/* Show ourself as "master brain" in the debug output */
	DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN	= 0x02,
	/* Disable mailbox renaming logic. This is just a kludge that should
	   be removed once the renaming logic has no more bugs.. */
	DSYNC_MAILBOX_TREES_SYNC_FLAG_NO_RENAMES	= 0x04
};

enum dsync_mailbox_node_existence {
	/* this is just a filler node for children or for
	   subscription deletion */
	DSYNC_MAILBOX_NODE_NONEXISTENT = 0,
	/* if mailbox GUID is set, the mailbox exists.
	   otherwise the directory exists. */
	DSYNC_MAILBOX_NODE_EXISTS,
	/* if mailbox GUID is set, the mailbox has been deleted.
	   otherwise the directory has been deleted. */
	DSYNC_MAILBOX_NODE_DELETED
};

struct dsync_mailbox_node {
	struct dsync_mailbox_node *parent, *next, *first_child;

	/* namespace where this node belongs to */
	struct mail_namespace *ns;
	/* this node's name (not including parents) */
	const char *name;
	/* mailbox GUID, or full of zeros if this is about a directory name */
	guid_128_t mailbox_guid;
	/* mailbox's UIDVALIDITY/UIDNEXT (may be 0 if not assigned yet) */
	uint32_t uid_validity, uid_next;

	/* existence of this mailbox/directory.
	   doesn't affect subscription state. */
	enum dsync_mailbox_node_existence existence;
	/* last time the mailbox/directory was created/renamed,
	   0 if not known */
	time_t last_renamed_or_created;

	/* last time the subscription state was changed, 0 if not known */
	time_t last_subscription_change;
	/* is this mailbox or directory subscribed? */
	unsigned int subscribed:1;

	/* Internal syncing flags: */
	unsigned int sync_delayed_guid_change:1;
	unsigned int sync_temporary_name:1;
};
ARRAY_DEFINE_TYPE(dsync_mailbox_node, struct dsync_mailbox_node *);

#define dsync_mailbox_node_guids_equal(node1, node2) \
	(memcmp((node1)->mailbox_guid, (node2)->mailbox_guid, \
		sizeof(guid_128_t)) == 0)

#define dsync_mailbox_node_is_dir(node) \
	guid_128_is_empty((node)->mailbox_guid)

enum dsync_mailbox_delete_type {
	/* Delete mailbox by given GUID */
	DSYNC_MAILBOX_DELETE_TYPE_MAILBOX = 1,
	/* Delete mailbox directory by given SHA1 name */
	DSYNC_MAILBOX_DELETE_TYPE_DIR,
	/* Unsubscribe mailbox by given SHA1 name */
	DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE,
};

struct dsync_mailbox_delete {
	enum dsync_mailbox_delete_type type;
	guid_128_t guid;
	time_t timestamp;
};

enum dsync_mailbox_tree_sync_type {
	DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX,
	DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR,
	DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX,
	DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR,
	/* Rename given mailbox name and its children */
	DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME,
	DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE,
	DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE
};

struct dsync_mailbox_tree_sync_change {
	enum dsync_mailbox_tree_sync_type type;

	/* for all types: */
	struct mail_namespace *ns;
	const char *full_name;

	/* for create_box and delete_box: */
	guid_128_t mailbox_guid;
	/* for create_box: */
	uint32_t uid_validity;
	/* for rename: */
	const char *rename_dest_name;
};

struct dsync_mailbox_tree *dsync_mailbox_tree_init(char sep, char alt_char);
void dsync_mailbox_tree_deinit(struct dsync_mailbox_tree **tree);

/* Lookup a mailbox node by name. Returns NULL if not known. */
struct dsync_mailbox_node *
dsync_mailbox_tree_lookup(struct dsync_mailbox_tree *tree,
			  const char *full_name);
/* Lookup a mailbox node by GUID. Returns NULL if not known.
   The mailbox GUID hash must have been build before calling this. */
struct dsync_mailbox_node *
dsync_mailbox_tree_lookup_guid(struct dsync_mailbox_tree *tree,
			       const guid_128_t guid);
/* Lookup or create a mailbox node by name. */
struct dsync_mailbox_node *
dsync_mailbox_tree_get(struct dsync_mailbox_tree *tree, const char *full_name);

/* Returns full name for the given mailbox node. */
const char *dsync_mailbox_node_get_full_name(const struct dsync_mailbox_tree *tree,
					     const struct dsync_mailbox_node *node);
void dsync_mailbox_node_append_full_name(string_t *str,
					 const struct dsync_mailbox_tree *tree,
					 const struct dsync_mailbox_node *node);

/* Copy everything from src to dest, except name and hierarchy pointers */
void dsync_mailbox_node_copy_data(struct dsync_mailbox_node *dest,
				  const struct dsync_mailbox_node *src);

/* Add nodes to tree from the given namespace. If box_name or box_guid is
   non-NULL, add only that mailbox to the tree. */
int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree,
			    struct mail_namespace *ns, const char *box_name,
			    const guid_128_t box_guid,
			    const char *const *exclude_mailboxes,
			    enum mail_error *error_r);

/* Return all known deleted mailboxes and directories. */
const struct dsync_mailbox_delete *
dsync_mailbox_tree_get_deletes(struct dsync_mailbox_tree *tree,
			       unsigned int *count_r);
/* Return mailbox node for a given delete record, or NULL if it doesn't exist.
   The delete record is intended to come from another tree, possibly with
   a different hierarchy separator. dsync_mailbox_tree_build_guid_hash() must
   have been called before this. */
struct dsync_mailbox_node *
dsync_mailbox_tree_find_delete(struct dsync_mailbox_tree *tree,
			       const struct dsync_mailbox_delete *del);
/* Build GUID lookup hash, if it's not already built. Returns 0 if ok, -1 if
   there are duplicate GUIDs. The nodes with the duplicate GUIDs are
   returned. */
int dsync_mailbox_tree_build_guid_hash(struct dsync_mailbox_tree *tree,
				       struct dsync_mailbox_node **dup_node1_r,
				       struct dsync_mailbox_node **dup_node2_r);
/* Manually add a new node to hash. */
int dsync_mailbox_tree_guid_hash_add(struct dsync_mailbox_tree *tree,
				     struct dsync_mailbox_node *node,
				     struct dsync_mailbox_node **old_node_r);
/* Set remote separator used for directory deletions in
   dsync_mailbox_tree_find_delete() */
void dsync_mailbox_tree_set_remote_sep(struct dsync_mailbox_tree *tree,
				       char remote_sep);

/* Iterate through all nodes in a tree (depth-first) */
struct dsync_mailbox_tree_iter *
dsync_mailbox_tree_iter_init(struct dsync_mailbox_tree *tree);
bool dsync_mailbox_tree_iter_next(struct dsync_mailbox_tree_iter *iter,
				  const char **full_name_r,
				  struct dsync_mailbox_node **node_r);
void dsync_mailbox_tree_iter_deinit(struct dsync_mailbox_tree_iter **iter);

/* Sync local and remote trees so at the end they're exactly the same.
   Return changes done to local tree. */
struct dsync_mailbox_tree_sync_ctx *
dsync_mailbox_trees_sync_init(struct dsync_mailbox_tree *local_tree,
			      struct dsync_mailbox_tree *remote_tree,
			      enum dsync_mailbox_trees_sync_type sync_type,
			      enum dsync_mailbox_trees_sync_flags sync_flags);
const struct dsync_mailbox_tree_sync_change *
dsync_mailbox_trees_sync_next(struct dsync_mailbox_tree_sync_ctx *ctx);
int dsync_mailbox_trees_sync_deinit(struct dsync_mailbox_tree_sync_ctx **ctx);

const char *dsync_mailbox_node_to_string(const struct dsync_mailbox_node *node);
const char *
dsync_mailbox_delete_type_to_string(enum dsync_mailbox_delete_type type);

#endif