[dovecot-cvs] dovecot/src/plugins/quota quota-dict.c, 1.20, 1.21 quota-maildir.c, 1.25, 1.26 quota-plugin.c, 1.7, 1.8 quota-private.h, 1.17, 1.18 quota-storage.c, 1.14, 1.15 quota.c, 1.20, 1.21 quota.h, 1.8, 1.9

tss at dovecot.org tss at dovecot.org
Tue Apr 17 18:33:27 EEST 2007


Update of /var/lib/cvs/dovecot/src/plugins/quota
In directory talvi:/tmp/cvs-serv30048/quota

Modified Files:
	quota-dict.c quota-maildir.c quota-plugin.c quota-private.h 
	quota-storage.c quota.c quota.h 
Log Message:
Use sync_notify() callback to count expunges. This makes the calculation
much more reliable than the previous method. Also did some API cleanups.



Index: quota-dict.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-dict.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- quota-dict.c	3 Apr 2007 04:44:34 -0000	1.20
+++ quota-dict.c	17 Apr 2007 15:33:23 -0000	1.21
@@ -119,7 +119,7 @@
 	else {
 		long long tmp;
 
-		/* recalculate quota if it's negative */
+		/* recalculate quota if it's negative or if it wasn't found */
 		tmp = ret == 0 ? -1 : strtoll(value, NULL, 10);
 		if (tmp < 0)
 			ret = dict_quota_count(root, want_bytes, value_r);
@@ -139,13 +139,19 @@
 	struct dict_transaction_context *dt;
 
 	dt = dict_transaction_begin(root->dict);
-	if (ctx->bytes_used != 0) {
-		dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
-				ctx->bytes_used);
-	}
-	if (ctx->count_used != 0) {
-		dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
-				ctx->count_used);
+
+	if (ctx->recalculate) {
+		dict_unset(dt, DICT_QUOTA_CURRENT_BYTES_PATH);
+		dict_unset(dt, DICT_QUOTA_CURRENT_COUNT_PATH);
+	} else {
+		if (ctx->bytes_used != 0) {
+			dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
+					ctx->bytes_used);
+		}
+		if (ctx->count_used != 0) {
+			dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
+					ctx->count_used);
+		}
 	}
 	
 	if (dict_transaction_commit(dt) < 0)

Index: quota-maildir.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-maildir.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -d -r1.25 -r1.26
--- quota-maildir.c	12 Mar 2007 21:05:50 -0000	1.25
+++ quota-maildir.c	17 Apr 2007 15:33:23 -0000	1.26
@@ -290,6 +290,18 @@
 	return ret;
 }
 
+static void maildirsize_rebuild_later(struct maildir_quota_root *root)
+{
+	if (!root->master_message_limits) {
+		/* FIXME: can't unlink(), because the limits would be lost. */
+		return;
+	}
+
+	if (unlink(root->maildirsize_path) < 0 &&
+	    errno != ENOENT && errno != ESTALE)
+		i_error("unlink(%s) failed: %m", root->maildirsize_path);
+}
+
 static int maildirsize_recalculate_finish(struct maildir_quota_root *root,
 					  int ret)
 {
@@ -297,14 +309,8 @@
 		/* maildir didn't change, we can write the maildirsize file */
 		ret = maildirsize_write(root, root->maildirsize_path);
 	}
-	if (ret != 0) {
-		/* make sure it gets rebuilt later */
-		if (unlink(root->maildirsize_path) < 0 &&
-		    errno != ENOENT && errno != ESTALE) {
-			i_error("unlink(%s) failed: %m",
-				root->maildirsize_path);
-		}
-	}
+	if (ret != 0)
+		maildirsize_rebuild_later(root);
 
 	return ret;
 }
@@ -655,11 +661,10 @@
 	struct maildir_quota_root *root =
 		(struct maildir_quota_root *) _root;
 
-	if (root->fd != -1) {
-		/* if writing fails, we don't care all that much */
-		(void)maildirsize_update(root, ctx->count_used,
-					 ctx->bytes_used);
-	}
+	if (root->fd == -1 || ctx->recalculate ||
+	    maildirsize_update(root, ctx->count_used, ctx->bytes_used) < 0)
+		maildirsize_rebuild_later(root);
+
 	return 0;
 }
 

Index: quota-plugin.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-plugin.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- quota-plugin.c	29 Mar 2007 07:59:14 -0000	1.7
+++ quota-plugin.c	17 Apr 2007 15:33:23 -0000	1.8
@@ -90,6 +90,6 @@
 			quota_next_hook_mail_storage_created;
 		hook_mailbox_list_created =
 			quota_next_hook_mailbox_list_created;
-		quota_deinit(quota_set);
+		quota_deinit(&quota_set);
 	}
 }

Index: quota-private.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-private.h,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- quota-private.h	29 Mar 2007 11:51:13 -0000	1.17
+++ quota-private.h	17 Apr 2007 15:33:23 -0000	1.18
@@ -79,6 +79,7 @@
 	struct mail *tmp_mail;
 
 	unsigned int failed:1;
+	unsigned int recalculate:1;
 };
 
 /* Register storage to all user's quota roots. */

Index: quota-storage.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-storage.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- quota-storage.c	29 Mar 2007 11:51:13 -0000	1.14
+++ quota-storage.c	17 Apr 2007 15:33:23 -0000	1.15
@@ -27,7 +27,13 @@
 struct quota_mailbox {
 	union mailbox_module_context module_ctx;
 
+	struct mailbox_transaction_context *expunge_trans;
+	struct quota_transaction_context *expunge_qt;
+	ARRAY_DEFINE(expunge_uids, uint32_t);
+	ARRAY_DEFINE(expunge_sizes, uoff_t);
+
 	unsigned int save_hack:1;
+	unsigned int recalculate:1;
 };
 
 static MODULE_CONTEXT_DEFINE_INIT(quota_storage_module,
@@ -39,14 +45,27 @@
 static int quota_mail_expunge(struct mail *_mail)
 {
 	struct mail_private *mail = (struct mail_private *)_mail;
+	struct quota_mailbox *qbox = QUOTA_CONTEXT(_mail->box);
 	union mail_module_context *qmail = QUOTA_MAIL_CONTEXT(mail);
-	struct quota_transaction_context *qt =
-		QUOTA_CONTEXT(_mail->transaction);
+	uoff_t size;
 
 	if (qmail->super.expunge(_mail) < 0)
 		return -1;
 
-	quota_free(qt, _mail);
+	/* We need to handle the situation where multiple transactions expunged
+	   the mail at the same time. In here we'll just save the message's
+	   physical size and do the quota freeing later when the message was
+	   known to be expunged. */
+	size = mail_get_physical_size(_mail);
+	if (size != (uoff_t)-1) {
+		if (!array_is_created(&qbox->expunge_uids)) {
+			i_array_init(&qbox->expunge_uids, 64);
+			i_array_init(&qbox->expunge_sizes, 64);
+		}
+		array_append(&qbox->expunge_uids, &_mail->uid, 1);
+		array_append(&qbox->expunge_sizes, &size, 1);
+	}
+
 	return 0;
 }
 
@@ -73,12 +92,12 @@
 	struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);
 
 	if (qbox->module_ctx.super.transaction_commit(ctx, flags) < 0) {
-		quota_transaction_rollback(qt);
+		quota_transaction_rollback(&qt);
 		return -1;
 	} else {
-		(void)quota_transaction_commit(qt);
 		if (qt->tmp_mail != NULL)
 			mail_free(&qt->tmp_mail);
+		(void)quota_transaction_commit(&qt);
 		return 0;
 	}
 }
@@ -93,7 +112,7 @@
 
 	if (qt->tmp_mail != NULL)
 		mail_free(&qt->tmp_mail);
-	quota_transaction_rollback(qt);
+	quota_transaction_rollback(&qt);
 }
 
 static struct mail *
@@ -229,6 +248,110 @@
 			   ctx->dest_mail : qt->tmp_mail);
 }
 
+static void quota_mailbox_sync_finish(struct quota_mailbox *qbox)
+{
+	if (array_is_created(&qbox->expunge_uids)) {
+		array_clear(&qbox->expunge_uids);
+		array_clear(&qbox->expunge_sizes);
+	}
+
+	if (qbox->expunge_qt != NULL) {
+		if (qbox->expunge_qt->tmp_mail != NULL) {
+			mail_free(&qbox->expunge_qt->tmp_mail);
+			mailbox_transaction_rollback(&qbox->expunge_trans);
+		}
+		(void)quota_transaction_commit(&qbox->expunge_qt);
+	}
+	qbox->recalculate = FALSE;
+}
+
+static void quota_mailbox_sync_notify(struct mailbox *box, uint32_t uid,
+				      enum mailbox_sync_type sync_type)
+{
+	struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
+	const uint32_t *uids;
+	const uoff_t *sizep;
+	unsigned int i, count;
+	uoff_t size;
+
+	if (qbox->module_ctx.super.sync_notify != NULL)
+		qbox->module_ctx.super.sync_notify(box, uid, sync_type);
+
+	if (sync_type != MAILBOX_SYNC_TYPE_EXPUNGE || qbox->recalculate) {
+		if (uid == 0)
+			quota_mailbox_sync_finish(qbox);
+		return;
+	}
+
+	/* we're in the middle of syncing the mailbox, so it's a bad idea to
+	   try and get the message sizes at this point. Rely on sizes that
+	   we saved earlier, or recalculate the whole quota if we don't know
+	   the size. */
+	if (!array_is_created(&qbox->expunge_uids)) {
+		i = count = 0;
+	} else {
+		uids = array_get(&qbox->expunge_uids, &count);
+		for (i = 0; i < count; i++) {
+			if (uids[i] == uid)
+				break;
+		}
+	}
+
+	if (qbox->expunge_qt == NULL)
+		qbox->expunge_qt = quota_transaction_begin(quota_set, box);
+
+	if (i != count) {
+		/* we already know the size */
+		sizep = array_idx(&qbox->expunge_sizes, i);
+		quota_free_bytes(qbox->expunge_qt, *sizep);
+		return;
+	}
+
+	/* try to look up the size. this works only if it's cached. */
+	if (qbox->expunge_qt->tmp_mail == NULL) {
+		qbox->expunge_trans = mailbox_transaction_begin(box, 0);
+		qbox->expunge_qt->tmp_mail =
+			mail_alloc(qbox->expunge_trans,
+				   MAIL_FETCH_PHYSICAL_SIZE, NULL);
+	}
+	mail_set_uid(qbox->expunge_qt->tmp_mail, uid);
+
+	size = mail_get_physical_size(qbox->expunge_qt->tmp_mail);
+	if (size != (uoff_t)-1)
+		quota_free_bytes(qbox->expunge_qt, size);
+	else {
+		/* there's no way to get the size. recalculate the quota. */
+		quota_recalculate(qbox->expunge_qt);
+		qbox->recalculate = TRUE;
+	}
+}
+
+static int quota_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
+				     enum mailbox_status_items status_items,
+				     struct mailbox_status *status_r)
+{
+	struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
+
+	/* just in case sync_notify() wasn't called with uid=0 */
+	quota_mailbox_sync_finish(qbox);
+
+	return qbox->module_ctx.super.sync_deinit(ctx, status_items, status_r);
+}
+
+static int quota_mailbox_close(struct mailbox *box)
+{
+	struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
+
+	if (array_is_created(&qbox->expunge_uids)) {
+		array_free(&qbox->expunge_uids);
+		array_free(&qbox->expunge_sizes);
+	}
+	i_assert(qbox->expunge_qt == NULL ||
+		 qbox->expunge_qt->tmp_mail == NULL);
+
+	return qbox->module_ctx.super.close(box);
+}
+
 static struct mailbox *
 quota_mailbox_open(struct mail_storage *storage, const char *name,
 		   struct istream *input, enum mailbox_open_flags flags)
@@ -251,6 +374,9 @@
 	box->v.save_init = quota_save_init;
 	box->v.save_finish = quota_save_finish;
 	box->v.copy = quota_copy;
+	box->v.sync_notify = quota_mailbox_sync_notify;
+	box->v.sync_deinit = quota_mailbox_sync_deinit;
+	box->v.close = quota_mailbox_close;
 	MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
 	return box;
 }
@@ -305,7 +431,8 @@
 
 	quota_remove_user_storage(quota_set, storage);
 
-	qstorage->super.destroy(storage);
+	if (qstorage->super.destroy != NULL)
+		qstorage->super.destroy(storage);
 }
 
 void quota_mail_storage_created(struct mail_storage *storage)

Index: quota.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- quota.c	29 Mar 2007 07:59:14 -0000	1.20
+++ quota.c	17 Apr 2007 15:33:23 -0000	1.21
@@ -50,13 +50,15 @@
 	return quota;
 }
 
-void quota_deinit(struct quota *quota)
+void quota_deinit(struct quota **_quota)
 {
+	struct quota *quota = *_quota;
 	struct quota_root **root;
 
+	*_quota = NULL;
 	while (array_count(&quota->roots) > 0) {
 		root = array_idx_modifiable(&quota->roots, 0);
-		quota_root_deinit(*root);
+		quota_root_deinit(root);
 	}
 
 	array_free(&quota->roots);
@@ -127,15 +129,16 @@
 
 	if (backend->v.init != NULL) {
 		if (backend->v.init(root, args) < 0) {
-			quota_root_deinit(root);
+			quota_root_deinit(&root);
 			return NULL;
 		}
 	}
 	return root;
 }
 
-void quota_root_deinit(struct quota_root *root)
+void quota_root_deinit(struct quota_root **_root)
 {
+	struct quota_root *root = *_root;
 	pool_t pool = root->pool;
 	struct quota_root *const *roots;
 	unsigned int i, count;
@@ -145,6 +148,7 @@
 		if (roots[i] == root)
 			array_delete(&root->quota->roots, i, 1);
 	}
+	*_root = NULL;
 
 	array_free(&root->rules);
 	array_free(&root->quota_module_contexts);
@@ -346,8 +350,11 @@
 	return root;
 }
 
-void quota_root_iter_deinit(struct quota_root_iter *iter)
+void quota_root_iter_deinit(struct quota_root_iter **_iter)
 {
+	struct quota_root_iter *iter = *_iter;
+
+	*_iter = NULL;
 	i_free(iter);
 }
 
@@ -467,12 +474,15 @@
 	return ctx;
 }
 
-int quota_transaction_commit(struct quota_transaction_context *ctx)
+int quota_transaction_commit(struct quota_transaction_context **_ctx)
 {
+	struct quota_transaction_context *ctx = *_ctx;
 	struct quota_root *const *roots;
 	unsigned int i, count;
 	int ret = 0;
 
+	*_ctx = NULL;
+
 	if (ctx->failed)
 		ret = -1;
 	else {
@@ -487,8 +497,11 @@
 	return ret;
 }
 
-void quota_transaction_rollback(struct quota_transaction_context *ctx)
+void quota_transaction_rollback(struct quota_transaction_context **_ctx)
 {
+	struct quota_transaction_context *ctx = *_ctx;
+
+	*_ctx = NULL;
 	i_free(ctx);
 }
 
@@ -560,8 +573,20 @@
 	uoff_t size;
 
 	size = mail_get_physical_size(mail);
-	if (size != (uoff_t)-1)
-		ctx->bytes_used -= size;
+	if (size == (uoff_t)-1)
+		quota_recalculate(ctx);
+	else
+		quota_free_bytes(ctx, size);
+}
 
+void quota_free_bytes(struct quota_transaction_context *ctx,
+		      uoff_t physical_size)
+{
+	ctx->bytes_used -= physical_size;
 	ctx->count_used--;
 }
+
+void quota_recalculate(struct quota_transaction_context *ctx)
+{
+	ctx->recalculate = TRUE;
+}

Index: quota.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- quota.h	22 Nov 2006 17:23:09 -0000	1.8
+++ quota.h	17 Apr 2007 15:33:23 -0000	1.9
@@ -17,11 +17,11 @@
 struct quota_transaction_context;
 
 struct quota *quota_init(void);
-void quota_deinit(struct quota *quota);
+void quota_deinit(struct quota **quota);
 
 /* Create a new quota root. */
 struct quota_root *quota_root_init(struct quota *quota, const char *root_def);
-void quota_root_deinit(struct quota_root *root);
+void quota_root_deinit(struct quota_root **root);
 
 /* Add a new rule too the quota root. Returns 0 if ok, -1 if rule is invalid. */
 int quota_root_add_rule(struct quota_root *root, const char *rule_def,
@@ -31,7 +31,7 @@
 struct quota_root_iter *
 quota_root_iter_init(struct quota *quota, struct mailbox *box);
 struct quota_root *quota_root_iter_next(struct quota_root_iter *iter);
-void quota_root_iter_deinit(struct quota_root_iter *iter);
+void quota_root_iter_deinit(struct quota_root_iter **iter);
 
 /* Return quota root or NULL. */
 struct quota_root *quota_root_lookup(struct quota *quota, const char *name);
@@ -52,9 +52,9 @@
 struct quota_transaction_context *quota_transaction_begin(struct quota *quota, 
 							  struct mailbox *box);
 /* Commit quota transaction. Returns 0 if ok, -1 if failed. */
-int quota_transaction_commit(struct quota_transaction_context *ctx);
+int quota_transaction_commit(struct quota_transaction_context **ctx);
 /* Rollback quota transaction changes. */
-void quota_transaction_rollback(struct quota_transaction_context *ctx);
+void quota_transaction_rollback(struct quota_transaction_context **ctx);
 
 /* Allocate from quota if there's space. Returns 1 if updated, 0 if not,
    -1 if error. If mail size is larger than even maximum allowed quota,
@@ -67,5 +67,9 @@
 /* Update quota by allocating/freeing space used by mail. */
 void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail);
 void quota_free(struct quota_transaction_context *ctx, struct mail *mail);
+void quota_free_bytes(struct quota_transaction_context *ctx,
+		      uoff_t physical_size);
+/* Mark the quota to be recalculated */
+void quota_recalculate(struct quota_transaction_context *ctx);
 
 #endif



More information about the dovecot-cvs mailing list