dovecot-2.0: lib-storage: Fixed handling hooks when more than 2 ...

dovecot at dovecot.org dovecot at dovecot.org
Fri Jul 16 20:20:23 EEST 2010


details:   http://hg.dovecot.org/dovecot-2.0/rev/6b66aad2a997
changeset: 11848:6b66aad2a997
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Jul 16 18:20:14 2010 +0100
description:
lib-storage: Fixed handling hooks when more than 2 were used.

diffstat:

 src/lib-storage/mail-storage-hooks.c |  174 ++++++++++++++++++++++------------
 1 files changed, 112 insertions(+), 62 deletions(-)

diffs (271 lines):

diff -r f590e5a76d7d -r 6b66aad2a997 src/lib-storage/mail-storage-hooks.c
--- a/src/lib-storage/mail-storage-hooks.c	Fri Jul 16 17:23:00 2010 +0100
+++ b/src/lib-storage/mail-storage-hooks.c	Fri Jul 16 18:20:14 2010 +0100
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "array.h"
+#include "llist.h"
 #include "module-dir.h"
 #include "mail-user.h"
 #include "mail-namespace.h"
@@ -13,6 +14,28 @@
 	const struct mail_storage_hooks *hooks;
 };
 
+struct hook_stack {
+	struct hook_stack *prev, *next;
+
+	/* Pointer to vfuncs struct. This assumes that a struct containing
+	   function pointers equals to an array of function pointers. Not
+	   ANSI-C, but should work in all OSes supported by Dovecot. Much
+	   easier anyway than doing this work manually.. */
+	void (**vfuncs)();
+	/* nonzero in the areas where vfuncs has been changed */
+	void (**mask)();
+};
+
+struct hook_build_context {
+	pool_t pool;
+	/* size of the vfuncs struct */
+	size_t size;
+	/* number of function pointers in the struct */
+	unsigned int count;
+
+	struct hook_stack *head, *tail;
+};
+
 static ARRAY_DEFINE(module_hooks,
 		    struct mail_storage_module_hooks) = ARRAY_INIT;
 static ARRAY_DEFINE(internal_hooks,
@@ -119,71 +142,102 @@
 	array_append_array(&user->hooks, &internal_hooks);
 }
 
-static void hook_vfuncs_update(void *_v, const void *_vlast,
-			       const void *_prev_vlast,
-			       void *_mask, size_t size)
+static void hook_build_append(struct hook_build_context *ctx, void (**vfuncs)())
 {
-	/* This function assumes that a struct containing function pointers
-	   equals to an array of function pointers. Not ANSI-C, but should work
-	   in all OSes supported by Dovecot. Much easier anyway than doing this
-	   work manually..
+	struct hook_stack *stack;
 
-	   The problem this function solves is:
+	stack = p_new(ctx->pool, struct hook_stack, 1);
+	stack->vfuncs = vfuncs;
+	stack->mask = p_malloc(ctx->pool, ctx->size);
+	DLLIST2_APPEND(&ctx->head, &ctx->tail, stack);
+}
 
-	   1. First hook overrides methods A and B by updating vlast->A+B.
-	      vlast points to v, so v->A+B gets updated.
-	   2. Second hook overrides method B and C by updating vlast->B+C.
-	      vlast points first hook's super struct. now, the call paths go:
-	       B: v->B = hook1_B, which calls its super.B = hook2_B,
-	          which calls super.B = original -> all OK
-	       C: v->C = still the original, so hook2_C won't be called!
-
-	   The idea is to detect the C situation, and update v->C = hook2_C
-	   so that the call path goes:
-	       C: v->C = hook2_C, which calls super.C = original
-	*/
-	void (**v)() = _v;
-	void (*const *prev_vlast)() = _prev_vlast;
-	void (*const *vlast)() = _vlast;
-	void (**mask)() = _mask;
-	unsigned int i, count;
+static struct hook_build_context *
+hook_build_init(void (**vfuncs)(), size_t size)
+{
+	struct hook_build_context *ctx;
+	pool_t pool;
 
 	i_assert((size % sizeof(void (*)())) == 0);
-	count = size / sizeof(void (*)());
 
-	for (i = 0; i < count; i++) {
-		if (mask[i] != NULL)
-			continue;
+	pool = pool_alloconly_create("hook build context", 1024);
+	ctx = p_new(pool, struct hook_build_context, 1);
+	ctx->pool = pool;
+	ctx->size = size;
+	ctx->count = size / sizeof(void (*)());
+	hook_build_append(ctx, vfuncs);
+	return ctx;
+}
 
-		if (v[i] != vlast[i]) {
-			/* first hook overriding any method in this object */
-			mask[i] = v[i];
-		} else if (prev_vlast != NULL && v[i] != prev_vlast[i]) {
-			/* first hook overriding this method object
-			   (but earlier hooks already overrode other methods) */
-			v[i] = prev_vlast[i];
-			mask[i] = prev_vlast[i];
+static void
+hook_update_mask(struct hook_build_context *ctx, struct hook_stack *stack,
+		 void (**vlast)())
+{
+	unsigned int i;
+
+	for (i = 0; i < ctx->count; i++) {
+		if (stack->vfuncs[i] != vlast[i])
+			stack->mask[i] = vlast[i];
+	}
+}
+
+static void
+hook_copy_stack(struct hook_build_context *ctx, struct hook_stack *stack)
+{
+	unsigned int i;
+
+	i_assert(stack->next != NULL);
+
+	for (i = 0; i < ctx->count; i++) {
+		if (stack->mask[i] == NULL) {
+			stack->vfuncs[i] = stack->next->vfuncs[i];
+			stack->mask[i] = stack->vfuncs[i];
 		}
 	}
 }
 
+static void hook_build_update(struct hook_build_context *ctx, void *_vlast)
+{
+	void (**vlast)() = _vlast;
+	struct hook_stack *stack;
+
+	/* ctx->vfuncs_stack->vfuncs points to the root vfuncs,
+	   ctx->vfuncs_stack->next->vfuncs points to the first super function
+	   that is being called, and so on.
+
+	   the previous plugin added its vfuncs to the stack tail.
+	   vlast contains the previous plugin's super vfuncs, which is where
+	   the next plugin should put its own vfuncs.
+
+	   first we'll need to figure out what vfuncs the previous plugin
+	   changed and update the mask */
+	hook_update_mask(ctx, ctx->tail, vlast);
+
+	/* now go up in the stack as long as the mask isn't set,
+	   and update the vfuncs */
+	for (stack = ctx->tail->prev; stack != NULL; stack = stack->prev)
+		hook_copy_stack(ctx, stack);
+
+	/* add vlast to stack */
+	hook_build_append(ctx, vlast);
+}
+
 void hook_mail_user_created(struct mail_user *user)
 {
 	const struct mail_storage_hooks *const *hooks;
-	struct mail_user_vfuncs mask, *prev_vlast = NULL;
+	struct hook_build_context *ctx;
 
 	mail_user_add_plugin_hooks(user);
 
-	memset(&mask, 0, sizeof(mask));
+	ctx = hook_build_init((void *)&user->v, sizeof(user->v));
 	user->vlast = &user->v;
 	array_foreach(&user->hooks, hooks) {
 		if ((*hooks)->mail_user_created != NULL) {
 			(*hooks)->mail_user_created(user);
-			hook_vfuncs_update(&user->v, user->vlast, prev_vlast,
-					   &mask, sizeof(mask));
-			prev_vlast = user->vlast;
+			hook_build_update(ctx, user->vlast);
 		}
 	}
+	pool_unref(&ctx->pool);
 }
 
 void hook_mail_namespace_storage_added(struct mail_namespace *ns)
@@ -209,52 +263,49 @@
 void hook_mail_storage_created(struct mail_storage *storage)
 {
 	const struct mail_storage_hooks *const *hooks;
-	struct mail_storage_vfuncs mask, *prev_vlast = NULL;
+	struct hook_build_context *ctx;
 
-	memset(&mask, 0, sizeof(mask));
+	ctx = hook_build_init((void *)&storage->v, sizeof(storage->v));
 	storage->vlast = &storage->v;
 	array_foreach(&storage->user->hooks, hooks) {
 		if ((*hooks)->mail_storage_created != NULL) {
 			(*hooks)->mail_storage_created(storage);
-			hook_vfuncs_update(&storage->v, storage->vlast,
-					   prev_vlast, &mask, sizeof(mask));
-			prev_vlast = storage->vlast;
+			hook_build_update(ctx, storage->vlast);
 		}
 	}
+	pool_unref(&ctx->pool);
 }
 
 void hook_mailbox_list_created(struct mailbox_list *list)
 {
 	const struct mail_storage_hooks *const *hooks;
-	struct mailbox_list_vfuncs mask, *prev_vlast = NULL;
+	struct hook_build_context *ctx;
 
-	memset(&mask, 0, sizeof(mask));
+	ctx = hook_build_init((void *)&list->v, sizeof(list->v));
 	list->vlast = &list->v;
 	array_foreach(&list->ns->user->hooks, hooks) {
 		if ((*hooks)->mailbox_list_created != NULL) {
 			(*hooks)->mailbox_list_created(list);
-			hook_vfuncs_update(&list->v, list->vlast, prev_vlast,
-					   &mask, sizeof(mask));
-			prev_vlast = list->vlast;
+			hook_build_update(ctx, list->vlast);
 		}
 	}
+	pool_unref(&ctx->pool);
 }
 
 void hook_mailbox_allocated(struct mailbox *box)
 {
 	const struct mail_storage_hooks *const *hooks;
-	struct mailbox_vfuncs mask, *prev_vlast = NULL;
+	struct hook_build_context *ctx;
 
-	memset(&mask, 0, sizeof(mask));
+	ctx = hook_build_init((void *)&box->v, sizeof(box->v));
 	box->vlast = &box->v;
 	array_foreach(&box->storage->user->hooks, hooks) {
 		if ((*hooks)->mailbox_allocated != NULL) {
 			(*hooks)->mailbox_allocated(box);
-			hook_vfuncs_update(&box->v, box->vlast, prev_vlast,
-					   &mask, sizeof(mask));
-			prev_vlast = box->vlast;
+			hook_build_update(ctx, box->vlast);
 		}
 	}
+	pool_unref(&ctx->pool);
 }
 
 void hook_mailbox_opened(struct mailbox *box)
@@ -271,16 +322,15 @@
 {
 	const struct mail_storage_hooks *const *hooks;
 	struct mail_private *pmail = (struct mail_private *)mail;
-	struct mail_vfuncs mask, *prev_vlast = NULL;
+	struct hook_build_context *ctx;
 
-	memset(&mask, 0, sizeof(mask));
+	ctx = hook_build_init((void *)&pmail->v, sizeof(pmail->v));
 	pmail->vlast = &pmail->v;
 	array_foreach(&mail->box->storage->user->hooks, hooks) {
 		if ((*hooks)->mail_allocated != NULL) {
 			(*hooks)->mail_allocated(mail);
-			hook_vfuncs_update(&pmail->v, pmail->vlast, prev_vlast,
-					   &mask, sizeof(mask));
-			prev_vlast = pmail->vlast;
+			hook_build_update(ctx, pmail->vlast);
 		}
 	}
+	pool_unref(&ctx->pool);
 }


More information about the dovecot-cvs mailing list