[Dovecot] per-mailbox message limits

Alan Brown
Tue, 29 May 2012 13:18:15 -0000

This is something Timo hacked up for me a few years ago and I realised
should be on the list in case anyone else wants them.

The following patches will limit maildir folders to 4000 messages on
Dovecot 2.0.* and 2.1.*

The Specfile patch is against the Cityfan Redhat EL5 SRPM but is likely
to work on most build platforms

Changing the message limit requires a recompile. It's brutal and ugly
but it prevents users overloading FSes that can't cope with huge numbers
of files (EG: GFS2)



-------------- next part --------------
--- dovecot.spec-2.1.6	2012-05-23 11:39:11.000000000 +0100
+++ dovecot.spec-2.1.6-mssl	2012-05-24 18:46:37.000000000 +0100
@@ -32,7 +32,7 @@
 # Remember to bump dovecot_release and sieve_release with every release
 %global dovecot_epoch 1
 %global dovecot_version 2.1.6
-%global dovecot_release
%{?prerel:0.}2.0%{?prerel:.%{prerel}}.cf.%{__distinit}%{__distvers}
+%global dovecot_release
%{?prerel:0.}2.0%{?prerel:.%{prerel}}.cf.MSSL.%{__distinit}%{__distvers}
 %global sieve_epoch 3
 %global sieve_version 0.3.0
 %global sieve_release 6.1.cf.%{__distinit}%{__distvers}
@@ -134,6 +134,7 @@
 Patch502:	dovecot-2.0-mysqlconfig.patch
 Patch503:	dovecot-2.1-pigeonhole-bzip2.patch
 Patch504:	dovecot-2.1.rc6-fallocate64.patch
+Patch505:	../../dovecot-mssl-2.1.6.mail-storage.c.DIFF
 BuildRequires:	gcc-c++
 BuildRequires:	openssl-devel, pam-devel, zlib-devel, bzip2-devel
 BuildRequires:	openldap-devel, krb5-devel >= 1.3
@@ -282,6 +283,11 @@
 # http://bugzilla.redhat.com/500487
 %patch504 -p1

+
+# add mssl maildir patch
+%patch505 -F3 -p1
+
+
 # PAM Configuration:
 # Default PAM configuration file uses password-auth common config;
 # revert to system-auth if password-auth is not available
-------------- next part --------------
--- dovecot-2.1.6/src/lib-storage/mail-storage.c.orig	2012-05-23
11:45:55.000000000 +0100
+++ dovecot-2.1.6/src/lib-storage/mail-storage.c	2012-05-23 12:11:28.000000000
+0100
@@ -22,12 +22,13 @@
 #include "mailbox-guid-cache.h"

 #include <stdlib.h>
 #include <ctype.h>

 #define MAILBOX_DELETE_RETRY_SECS (60*5)
+#define MAX_MSGS_PER_MAILBOX 4000

 extern struct mail_search_register *mail_search_register_imap;
 extern struct mail_search_register *mail_search_register_human;

 struct mail_storage_module_register mail_storage_module_register = { 0 };
 struct mail_module_register mail_module_register = { 0 };
@@ -1620,22 +1621,28 @@
 	ctx->dest_mail = mail;
 }

 int mailbox_save_begin(struct mail_save_context **ctx, struct istream *input)
 {
 	struct mailbox *box = (*ctx)->transaction->box;
+	struct mailbox_status status;
 	int ret;

 	if (mail_index_is_deleted(box->index)) {
 		mailbox_set_deleted(box);
 		return -1;
 	}

 	if (!(*ctx)->copying_via_save)
 		(*ctx)->saving = TRUE;
-	if (box->v.save_begin == NULL) {
+	mailbox_get_status(box, STATUS_MESSAGES, &status);
+	if (status.messages >= MAX_MSGS_PER_MAILBOX) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOSPACE,
+					"Mailbox full: Too many messages");
+		ret = -1;
+	} else if (box->v.save_begin == NULL) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
 				       "Saving messages not supported");
 		ret = -1;
 	} else {
 		ret = box->v.save_begin(*ctx, input);
 	}
@@ -1685,23 +1692,31 @@

 int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail)
 {
 	struct mail_save_context *ctx = *_ctx;
 	struct mailbox *box = ctx->transaction->box;
 	struct mail_keywords *keywords = ctx->keywords;
+	struct mailbox_status status;
 	int ret;

 	*_ctx = NULL;

 	if (mail_index_is_deleted(box->index)) {
 		mailbox_set_deleted(box);
 		mailbox_save_cancel(&ctx);
 		return -1;
 	}

-	ret = ctx->transaction->box->v.copy(ctx, mail);
+	mailbox_get_status(box, STATUS_MESSAGES, &status);
+	if (status.messages >= MAX_MSGS_PER_MAILBOX) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOSPACE,
+					"Mailbox full: Too many messages");
+		ret = -1;
+	} else {
+		ret = ctx->transaction->box->v.copy(ctx, mail);
+	}
 	if (keywords != NULL)
 		mailbox_keywords_unref(&keywords);
 	return ret;
 }

 int mailbox_save_using_mail(struct mail_save_context **ctx, struct mail *mail)
-------------- next part --------------
--- dovecot-2.0.7/src/lib-storage/mail-storage.c.ORIG	2010-11-11
19:46:14.000000000 +0000
+++ dovecot-2.0.7/src/lib-storage/mail-storage.c	2010-11-11 19:46:19.000000000
+0000
@@ -15,20 +15,21 @@
 #include "mail-storage-settings.h"
 #include "mail-namespace.h"
 #include "mail-search.h"
 #include "mail-search-register.h"
 #include "mailbox-search-result-private.h"

 #include <stdlib.h>
 #include <ctype.h>

 #define MAILBOX_DELETE_RETRY_SECS (60*5)
+#define MAX_MSGS_PER_MAILBOX 4000

 extern struct mail_search_register *mail_search_register_imap;
 extern struct mail_search_register *mail_search_register_human;

 struct mail_storage_module_register mail_storage_module_register = { 0 };
 struct mail_module_register mail_module_register = { 0 };

 struct mail_storage_mail_index_module mail_storage_mail_index_module
MODULE_CONTEXT_INIT(&mail_index_module_register);
 ARRAY_TYPE(mail_storage) mail_storage_classes;
@@ -1365,28 +1366,34 @@ void mailbox_save_set_pop3_uidl(struct m

 void mailbox_save_set_dest_mail(struct mail_save_context *ctx,
 				struct mail *mail)
 {
 	ctx->dest_mail = mail;
 }

 int mailbox_save_begin(struct mail_save_context **ctx, struct istream *input)
 {
 	struct mailbox *box = (*ctx)->transaction->box;
+	struct mailbox_status status;
 	int ret;

 	if (mail_index_is_deleted(box->index)) {
 		mailbox_set_deleted(box);
 		return -1;
 	}

-	if (box->v.save_begin == NULL) {
+	mailbox_get_status(box, STATUS_MESSAGES, &status);
+	if (status.messages >= MAX_MSGS_PER_MAILBOX) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOSPACE,
+				       "Mailbox full: Too many messages");
+		ret = -1;
+	} else if (box->v.save_begin == NULL) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
 				       "Saving messages not supported");
 		ret = -1;
 	} else {
 		ret = box->v.save_begin(*ctx, input);
 	}

 	if (ret < 0) {
 		mailbox_save_cancel(ctx);
 		return -1;
@@ -1423,31 +1430,39 @@ void mailbox_save_cancel(struct mail_sav
 	ctx->transaction->box->v.save_cancel(ctx);
 	if (keywords != NULL)
 		mailbox_keywords_unref(box, &keywords);
 }

 int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail)
 {
 	struct mail_save_context *ctx = *_ctx;
 	struct mailbox *box = ctx->transaction->box;
 	struct mail_keywords *keywords = ctx->keywords;
+	struct mailbox_status status;
 	int ret;

 	*_ctx = NULL;

 	if (mail_index_is_deleted(box->index)) {
 		mailbox_set_deleted(box);
 		mailbox_save_cancel(_ctx);
 		return -1;
 	}

-	ret = ctx->transaction->box->v.copy(ctx, mail);
+	mailbox_get_status(box, STATUS_MESSAGES, &status);
+	if (status.messages >= MAX_MSGS_PER_MAILBOX) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOSPACE,
+				       "Mailbox full: Too many messages");
+		ret = -1;
+	} else {
+		ret = ctx->transaction->box->v.copy(ctx, mail);
+	}
 	if (keywords != NULL)
 		mailbox_keywords_unref(box, &keywords);
 	return ret;
 }

 bool mailbox_is_inconsistent(struct mailbox *box)
 {
 	return box->mailbox_deleted || box->v.is_inconsistent(box);
 }