1 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "llist.h"
6 #include "hook-build.h"
7 
8 struct hook_stack {
9 	struct hook_stack *prev, *next;
10 
11 	/* Pointer to vfuncs struct. This assumes that a struct containing
12 	   function pointers equals to an array of function pointers. Not
13 	   ANSI-C, but should work in all OSes supported by Dovecot. Much
14 	   easier anyway than doing this work manually.. */
15 	void (**vfuncs)();
16 	/* nonzero in the areas where vfuncs has been changed */
17 	void (**mask)();
18 };
19 
20 struct hook_build_context {
21 	pool_t pool;
22 	/* size of the vfuncs struct */
23 	size_t size;
24 	/* number of function pointers in the struct */
25 	unsigned int count;
26 
27 	struct hook_stack *head, *tail;
28 };
29 
hook_build_append(struct hook_build_context * ctx,void (** vfuncs)())30 static void hook_build_append(struct hook_build_context *ctx, void (**vfuncs)())
31 {
32 	struct hook_stack *stack;
33 
34 	stack = p_new(ctx->pool, struct hook_stack, 1);
35 	stack->vfuncs = vfuncs;
36 	stack->mask = p_malloc(ctx->pool, ctx->size);
37 	DLLIST2_APPEND(&ctx->head, &ctx->tail, stack);
38 }
39 
hook_build_init(void (** vfuncs)(),size_t size)40 struct hook_build_context *hook_build_init(void (**vfuncs)(), size_t size)
41 {
42 	struct hook_build_context *ctx;
43 	pool_t pool;
44 
45 	i_assert((size % sizeof(void (*)())) == 0);
46 
47 	pool = pool_alloconly_create("hook build context", 2048);
48 	ctx = p_new(pool, struct hook_build_context, 1);
49 	ctx->pool = pool;
50 	ctx->size = size;
51 	ctx->count = size / sizeof(void (*)());
52 	hook_build_append(ctx, vfuncs);
53 	return ctx;
54 }
55 
56 static void
hook_update_mask(struct hook_build_context * ctx,struct hook_stack * stack,void (** vlast)())57 hook_update_mask(struct hook_build_context *ctx, struct hook_stack *stack,
58 		 void (**vlast)())
59 {
60 	unsigned int i;
61 
62 	for (i = 0; i < ctx->count; i++) {
63 		if (stack->vfuncs[i] != vlast[i]) {
64 			i_assert(stack->vfuncs[i] != NULL);
65 			stack->mask[i] = stack->vfuncs[i];
66 		}
67 	}
68 }
69 
70 static void
hook_copy_stack(struct hook_build_context * ctx,struct hook_stack * stack)71 hook_copy_stack(struct hook_build_context *ctx, struct hook_stack *stack)
72 {
73 	unsigned int i;
74 
75 	i_assert(stack->next != NULL);
76 
77 	for (i = 0; i < ctx->count; i++) {
78 		if (stack->mask[i] == NULL) {
79 			stack->vfuncs[i] = stack->next->vfuncs[i];
80 			stack->mask[i] = stack->next->mask[i];
81 		}
82 	}
83 }
84 
hook_build_update(struct hook_build_context * ctx,void * _vlast)85 void hook_build_update(struct hook_build_context *ctx, void *_vlast)
86 {
87 	void (**vlast)() = _vlast;
88 	struct hook_stack *stack;
89 
90 	if (ctx->tail->vfuncs == vlast) {
91 		/* no vfuncs overridden */
92 		return;
93 	}
94 
95 	/* ctx->vfuncs_stack->vfuncs points to the root vfuncs,
96 	   ctx->vfuncs_stack->next->vfuncs points to the first super function
97 	   that is being called, and so on.
98 
99 	   the previous plugin added its vfuncs to the stack tail.
100 	   vlast contains the previous plugin's super vfuncs, which is where
101 	   the next plugin should put its own vfuncs.
102 
103 	   first we'll need to figure out what vfuncs the previous plugin
104 	   changed and update the mask */
105 	hook_update_mask(ctx, ctx->tail, vlast);
106 
107 	/* now go up in the stack as long as the mask isn't set,
108 	   and update the vfuncs */
109 	for (stack = ctx->tail->prev; stack != NULL; stack = stack->prev)
110 		hook_copy_stack(ctx, stack);
111 
112 	/* add vlast to stack */
113 	hook_build_append(ctx, vlast);
114 }
115 
hook_build_deinit(struct hook_build_context ** _ctx)116 void hook_build_deinit(struct hook_build_context **_ctx)
117 {
118 	struct hook_build_context *ctx = *_ctx;
119 	*_ctx = NULL;
120 	pool_unref(&ctx->pool);
121 }
122