[dovecot-cvs] dovecot/src/plugins/quota quota-dict.c, 1.13, 1.14 quota-dirsize.c, 1.12, 1.13 quota-fs.c, 1.18, 1.19 quota-maildir.c, 1.12, 1.13 quota-plugin.c, 1.3, 1.4 quota-private.h, 1.10, 1.11 quota-storage.c, 1.10, 1.11 quota.c, 1.11, 1.12 quota.h, 1.5, 1.6
tss-movial at dovecot.org
tss-movial at dovecot.org
Sun Jul 30 20:58:46 EEST 2006
Update of /var/lib/cvs/dovecot/src/plugins/quota
In directory talvi:/tmp/cvs-serv31457/src/plugins/quota
Modified Files:
quota-dict.c quota-dirsize.c quota-fs.c quota-maildir.c
quota-plugin.c quota-private.h quota-storage.c quota.c quota.h
Log Message:
Added support for multiple quota roots, rules and storages. The configuration
has changed, userdb now needs to return "quota_rule" settings instead. Much
of the code changes were done by Tianyan Liu.
Index: quota-dict.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-dict.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- quota-dict.c 17 Jun 2006 12:21:21 -0000 1.13
+++ quota-dict.c 30 Jul 2006 17:58:43 -0000 1.14
@@ -14,113 +14,76 @@
struct dict_quota_root {
struct quota_root root;
struct dict *dict;
-
- uint64_t message_bytes_limit;
- uint64_t message_count_limit;
};
extern struct quota_backend quota_backend_dict;
-static struct quota_root *
-dict_quota_init(struct quota_setup *setup, const char *name)
+static struct quota_root *dict_quota_alloc(void)
{
struct dict_quota_root *root;
- struct dict *dict;
- const char *uri, *const *args;
- unsigned long long message_bytes_limit = 0, message_count_limit = 0;
- uri = strchr(setup->data, ' ');
- if (uri == NULL) {
- i_error("dict quota: URI missing from parameters: %s",
- setup->data);
- return NULL;
- }
+ root = i_new(struct dict_quota_root, 1);
+ return &root->root;
+}
- t_push();
- args = t_strsplit(t_strdup_until(setup->data, uri++), ":");
- for (; *args != '\0'; args++) {
- if (strncmp(*args, "storage=", 8) == 0) {
- message_bytes_limit =
- strtoull(*args + 8, NULL, 10) * 1024;
- } else if (strncmp(*args, "messages=", 9) == 0)
- message_bytes_limit = strtoull(*args + 9, NULL, 10);
- }
- t_pop();
+static int dict_quota_init(struct quota_root *_root, const char *args)
+{
+ struct dict_quota_root *root = (struct dict_quota_root *)_root;
+ const char *username, *p;
- if (getenv("DEBUG") != NULL) {
- i_info("dict quota: uri = %s", uri);
- i_info("dict quota: byte limit = %llu", message_bytes_limit);
- i_info("dict quota: count limit = %llu", message_count_limit);
+ p = args == NULL ? NULL : strchr(args, ':');
+ if (p == NULL) {
+ i_error("dict quota: URI missing from parameters");
+ return -1;
}
- dict = dict_init(uri, getenv("USER"));
- if (dict == NULL)
- return NULL;
+ username = t_strdup_until(args, p);
+ args = p+1;
- root = i_new(struct dict_quota_root, 1);
- root->root.name = i_strdup(name);
- root->root.v = quota_backend_dict.v;
- root->dict = dict;
+ if (*username == '\0')
+ username = getenv("USER");
- root->message_bytes_limit =
- message_bytes_limit == 0 ? (uint64_t)-1 : message_bytes_limit;
- root->message_count_limit =
- message_count_limit == 0 ? (uint64_t)-1 : message_count_limit;
- return &root->root;
+ if (getenv("DEBUG") != NULL)
+ i_info("dict quota: uri = %s", args);
+
+ root->dict = dict_init(args, username);
+ return root->dict != NULL ? 0 : -1;
}
static void dict_quota_deinit(struct quota_root *_root)
{
struct dict_quota_root *root = (struct dict_quota_root *)_root;
- i_free(root->root.name);
+ if (root->dict != NULL)
+ dict_deinit(&root->dict);
i_free(root);
}
-static bool
-dict_quota_add_storage(struct quota_root *root __attr_unused__,
- struct mail_storage *storage __attr_unused__)
-{
- return TRUE;
-}
-
-static void
-dict_quota_remove_storage(struct quota_root *root __attr_unused__,
- struct mail_storage *storage __attr_unused__)
-{
-}
-
static const char *const *
dict_quota_root_get_resources(struct quota_root *root __attr_unused__)
{
- static const char *resources[] = { QUOTA_NAME_STORAGE, NULL };
+ static const char *resources[] = {
+ QUOTA_NAME_STORAGE, QUOTA_NAME_MESSAGES, NULL
+ };
return resources;
}
static int
dict_quota_get_resource(struct quota_root *_root, const char *name,
- uint64_t *value_r, uint64_t *limit_r)
+ uint64_t *value_r, uint64_t *limit __attr_unused__)
{
struct dict_quota_root *root = (struct dict_quota_root *)_root;
const char *value;
int ret;
if (strcmp(name, QUOTA_NAME_STORAGE) == 0) {
- if (root->message_bytes_limit == (uint64_t)-1)
- return 0;
-
- *limit_r = root->message_bytes_limit / 1024;
t_push();
ret = dict_lookup(root->dict, unsafe_data_stack_pool,
DICT_QUOTA_CURRENT_BYTES_PATH, &value);
*value_r = ret <= 0 ? 0 : strtoull(value, NULL, 10) / 1024;
t_pop();
} else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
- if (root->message_count_limit == (uint64_t)-1)
- return 0;
-
- *limit_r = root->message_count_limit;
t_push();
ret = dict_lookup(root->dict, unsafe_data_stack_pool,
DICT_QUOTA_CURRENT_COUNT_PATH, &value);
@@ -130,101 +93,37 @@
return 0;
}
- return 1;
-}
-
-static int
-dict_quota_set_resource(struct quota_root *root,
- const char *name __attr_unused__,
- uint64_t value __attr_unused__)
-{
- quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
- return -1;
-}
-
-static struct quota_root_transaction_context *
-dict_quota_transaction_begin(struct quota_root *_root,
- struct quota_transaction_context *_ctx)
-{
- struct dict_quota_root *root = (struct dict_quota_root *)_root;
- struct quota_root_transaction_context *ctx;
- const char *value;
-
- ctx = i_new(struct quota_root_transaction_context, 1);
- ctx->root = _root;
- ctx->ctx = _ctx;
-
- ctx->bytes_limit = root->message_bytes_limit;
- ctx->count_limit = root->message_count_limit;
-
- t_push();
- if (ctx->bytes_limit != (uint64_t)-1) {
- (void)dict_lookup(root->dict, unsafe_data_stack_pool,
- DICT_QUOTA_CURRENT_BYTES_PATH, &value);
- ctx->bytes_current = value == NULL ? 0 :
- strtoull(value, NULL, 10);
- }
- if (ctx->count_limit != (uint64_t)-1) {
- (void)dict_lookup(root->dict, unsafe_data_stack_pool,
- DICT_QUOTA_CURRENT_COUNT_PATH, &value);
- ctx->count_current = value == NULL ? 0 :
- strtoull(value, NULL, 10);
- }
- t_pop();
- return ctx;
+ return ret;
}
static int
-dict_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+dict_quota_update(struct quota_root *_root,
+ struct quota_transaction_context *ctx)
{
- struct dict_quota_root *root = (struct dict_quota_root *)ctx->root;
+ struct dict_quota_root *root = (struct dict_quota_root *) _root;
struct dict_transaction_context *dt;
dt = dict_transaction_begin(root->dict);
- if (ctx->bytes_limit != (uint64_t)-1) {
- dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
- ctx->bytes_diff);
- }
- if (ctx->count_limit != (uint64_t)-1) {
- dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
- ctx->count_diff);
- }
+ dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
+ ctx->bytes_used);
+ dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
+ ctx->count_used);
+
if (dict_transaction_commit(dt) < 0)
- i_error("dict_quota: Couldn't update quota");
-
- i_free(ctx);
+ return -1;
return 0;
}
-static void
-dict_quota_transaction_rollback(struct quota_root_transaction_context *ctx)
-{
- i_free(ctx);
-}
-
struct quota_backend quota_backend_dict = {
"dict",
{
+ dict_quota_alloc,
dict_quota_init,
dict_quota_deinit,
-
- dict_quota_add_storage,
- dict_quota_remove_storage,
-
+ NULL,
dict_quota_root_get_resources,
-
dict_quota_get_resource,
- dict_quota_set_resource,
-
- dict_quota_transaction_begin,
- dict_quota_transaction_commit,
- dict_quota_transaction_rollback,
-
- quota_default_try_alloc,
- quota_default_try_alloc_bytes,
- quota_default_test_alloc_bytes,
- quota_default_alloc,
- quota_default_free
+ dict_quota_update
}
};
Index: quota-dirsize.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-dirsize.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- quota-dirsize.c 28 Jun 2006 13:11:00 -0000 1.12
+++ quota-dirsize.c 30 Jul 2006 17:58:43 -0000 1.13
@@ -13,60 +13,22 @@
#include <dirent.h>
#include <sys/stat.h>
-struct dirsize_quota_root {
- struct quota_root root;
-
- uint64_t storage_limit;
+struct quota_count_path {
+ const char *path;
+ bool is_file;
};
+ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path);
extern struct quota_backend quota_backend_dirsize;
-static struct quota_root *
-dirsize_quota_init(struct quota_setup *setup, const char *name)
+static struct quota_root *dirsize_quota_alloc(void)
{
- struct dirsize_quota_root *root;
- const char *const *args;
-
- root = i_new(struct dirsize_quota_root, 1);
- root->root.name = i_strdup(name);
- root->root.v = quota_backend_dirsize.v;
-
- t_push();
- args = t_strsplit(setup->data, ":");
-
- for (; *args != '\0'; args++) {
- if (strncmp(*args, "storage=", 8) == 0)
- root->storage_limit = strtoull(*args + 8, NULL, 10);
- }
- t_pop();
-
- if (getenv("DEBUG") != NULL) {
- i_info("dirsize quota limit = %llukB",
- (unsigned long long)root->storage_limit);
- }
-
- return &root->root;
+ return i_new(struct quota_root, 1);
}
static void dirsize_quota_deinit(struct quota_root *_root)
{
- struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
-
- i_free(root->root.name);
- i_free(root);
-}
-
-static bool
-dirsize_quota_add_storage(struct quota_root *root __attr_unused__,
- struct mail_storage *storage __attr_unused__)
-{
- return TRUE;
-}
-
-static void
-dirsize_quota_remove_storage(struct quota_root *root __attr_unused__,
- struct mail_storage *storage __attr_unused__)
-{
+ i_free(_root);
}
static const char *const *
@@ -133,8 +95,7 @@
return ret;
}
-static int get_usage(struct dirsize_quota_root *root,
- const char *path, bool is_file, uint64_t *value_r)
+static int get_usage(const char *path, bool is_file, uint64_t *value_r)
{
struct stat st;
@@ -148,21 +109,12 @@
}
*value_r += st.st_size;
} else {
- if (get_dir_usage(path, value_r) < 0) {
- quota_set_error(root->root.setup->quota,
- "Internal quota calculation error");
+ if (get_dir_usage(path, value_r) < 0)
return -1;
- }
}
return 0;
}
-struct quota_count_path {
- const char *path;
- bool is_file;
-};
-ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path);
-
static void quota_count_path_add(ARRAY_TYPE(quota_count_path) *paths,
const char *path, bool is_file)
{
@@ -173,7 +125,7 @@
for (i = 0; i < count; i++) {
if (strncmp(count_path[i].path, path,
strlen(count_path[i].path)) == 0) {
- /* this path is already being counted */
+ /* this path has already been counted */
return;
}
if (strncmp(count_path[i].path, path, strlen(path)) == 0) {
@@ -191,7 +143,7 @@
}
static int
-get_quota_root_usage(struct dirsize_quota_root *root, uint64_t *value_r)
+get_quota_root_usage(struct quota_root *root, uint64_t *value_r)
{
struct mail_storage *const *storages;
ARRAY_TYPE(quota_count_path) paths;
@@ -203,7 +155,7 @@
t_push();
ARRAY_CREATE(&paths, pool_datastack_create(),
struct quota_count_path, 8);
- storages = array_get(&root->root.storages, &count);
+ storages = array_get(&root->quota->storages, &count);
for (i = 0; i < count; i++) {
path = mail_storage_get_mailbox_path(storages[i], "", &is_file);
quota_count_path_add(&paths, path, is_file);
@@ -217,7 +169,7 @@
/* now sum up the found paths */
count_paths = array_get(&paths, &count);
for (i = 0; i < count; i++) {
- if (get_usage(root, count_paths[i].path, count_paths[i].is_file,
+ if (get_usage(count_paths[i].path, count_paths[i].is_file,
value_r) < 0) {
t_pop();
return -1;
@@ -225,97 +177,40 @@
}
t_pop();
-
return 0;
}
static int
dirsize_quota_get_resource(struct quota_root *_root, const char *name,
- uint64_t *value_r, uint64_t *limit_r)
+ uint64_t *value_r, uint64_t *limit __attr_unused__)
{
- struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
-
- *value_r = 0;
- *limit_r = 0;
-
if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0)
return 0;
- if (get_quota_root_usage(root, value_r) < 0)
+ if (get_quota_root_usage(_root, value_r) < 0)
return -1;
*value_r /= 1024;
- *limit_r = root->storage_limit;
return 1;
}
-static int
-dirsize_quota_set_resource(struct quota_root *root,
- const char *name __attr_unused__,
- uint64_t value __attr_unused__)
-{
- quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
- return -1;
-}
-
-static struct quota_root_transaction_context *
-dirsize_quota_transaction_begin(struct quota_root *_root,
- struct quota_transaction_context *_ctx)
-{
- struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
- struct quota_root_transaction_context *ctx;
-
- ctx = i_new(struct quota_root_transaction_context, 1);
- ctx->root = _root;
- ctx->ctx = _ctx;
-
- /* Get dir usage only once at the beginning of transaction.
- When copying/appending lots of mails we don't want to re-read the
- entire directory structure after each mail. */
- if (get_quota_root_usage(root, &ctx->bytes_current) < 0 ||
- ctx->bytes_current == (uint64_t)-1) {
- ctx->bytes_current = (uint64_t)-1;
- quota_set_error(_root->setup->quota,
- "Internal quota calculation error");
- }
-
- ctx->bytes_limit = root->storage_limit * 1024;
- ctx->count_limit = (uint64_t)-1;
- return ctx;
-}
-
-static int
-dirsize_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+static int
+dirsize_quota_update(struct quota_root *root __attr_unused__,
+ struct quota_transaction_context *ctx __attr_unused__)
{
- int ret = ctx->bytes_current == (uint64_t)-1 ? -1 : 0;
-
- i_free(ctx);
- return ret;
+ return 0;
}
struct quota_backend quota_backend_dirsize = {
"dirsize",
{
- dirsize_quota_init,
+ dirsize_quota_alloc,
+ NULL,
dirsize_quota_deinit,
-
- dirsize_quota_add_storage,
- dirsize_quota_remove_storage,
-
+ NULL,
dirsize_quota_root_get_resources,
-
dirsize_quota_get_resource,
- dirsize_quota_set_resource,
-
- dirsize_quota_transaction_begin,
- dirsize_quota_transaction_commit,
- quota_default_transaction_rollback,
-
- quota_default_try_alloc,
- quota_default_try_alloc_bytes,
- quota_default_test_alloc_bytes,
- quota_default_alloc,
- quota_default_free
+ dirsize_quota_update
}
};
Index: quota-fs.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-fs.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- quota-fs.c 1 Jul 2006 20:13:49 -0000 1.18
+++ quota-fs.c 30 Jul 2006 17:58:43 -0000 1.19
@@ -46,22 +46,13 @@
struct fs_quota_mountpoint *mount;
};
-struct fs_quota_root_iter {
- struct quota_root_iter iter;
-
- bool sent;
-};
-
extern struct quota_backend quota_backend_fs;
-static struct quota_root *
-fs_quota_init(struct quota_setup *setup __attr_unused__, const char *name)
+static struct quota_root *fs_quota_alloc(void)
{
struct fs_quota_root *root;
root = i_new(struct fs_quota_root, 1);
- root->root.name = i_strdup(name);
- root->root.v = quota_backend_fs.v;
root->uid = geteuid();
return &root->root;
@@ -89,7 +80,6 @@
if (root->mount != NULL)
fs_quota_mountpoint_free(root->mount);
- i_free(root->root.name);
i_free(root);
}
@@ -110,42 +100,66 @@
return mount;
}
-static bool fs_quota_add_storage(struct quota_root *_root,
- struct mail_storage *storage)
+static struct fs_quota_root *
+fs_quota_root_find_mountpoint(struct quota *quota,
+ const struct fs_quota_mountpoint *mount)
+{
+ struct quota_root *const *roots;
+ struct fs_quota_root *empty = NULL;
+ unsigned int i, count;
+
+ roots = array_get("a->roots, &count);
+ for (i = 0; i < count; i++) {
+ if (roots[i]->backend == "a_backend_fs) {
+ struct fs_quota_root *root =
+ (struct fs_quota_root *)roots[i];
+
+ if (root->mount == NULL)
+ empty = root;
+ else if (strcmp(root->mount->mount_path,
+ mount->mount_path) == 0)
+ return root;
+ }
+ }
+ return empty;
+}
+
+static void fs_quota_storage_added(struct quota *quota,
+ struct mail_storage *storage)
{
- struct fs_quota_root *root = (struct fs_quota_root *)_root;
struct fs_quota_mountpoint *mount;
+ struct quota_root *_root;
+ struct fs_quota_root *root;
const char *dir;
bool is_file;
dir = mail_storage_get_mailbox_path(storage, "", &is_file);
-
- if (getenv("DEBUG") != NULL)
- i_info("fs quota add storage dir = %s", dir);
-
mount = fs_quota_mountpoint_get(dir);
- if (root->mount == NULL) {
- if (mount == NULL) {
- /* Not found */
- return TRUE;
- }
- root->mount = mount;
- } else {
- bool match = strcmp(root->mount->mount_path,
- mount->mount_path) == 0;
+ if (getenv("DEBUG") != NULL) {
+ i_info("fs quota add storage dir = %s", dir);
+ i_info("fs quota block device = %s", mount->device_path);
+ i_info("fs quota mount point = %s", mount->mount_path);
+ }
+ root = fs_quota_root_find_mountpoint(quota, mount);
+ if (root != NULL && root->mount != NULL) {
+ /* already exists */
fs_quota_mountpoint_free(mount);
- if (!match) {
- /* different mountpoints, can't use this */
- return FALSE;
- }
- mount = root->mount;
+ return;
}
- if (getenv("DEBUG") != NULL) {
- i_info("fs quota block device = %s", mount->device_path);
- i_info("fs quota mount point = %s", mount->mount_path);
+ if (root == NULL) {
+ /* create a new root for this mountpoint */
+ _root = quota_root_init(quota, quota_backend_fs.name);
+ root = (struct fs_quota_root *)_root;
+ root->root.name =
+ p_strdup_printf(root->root.pool, "%s%d",
+ quota_backend_fs.name,
+ array_count("a->roots));
+ } else {
+ /* this is the default root. */
}
+ root->mount = mount;
#ifdef HAVE_Q_QUOTACTL
if (mount->path == NULL) {
@@ -155,13 +169,6 @@
i_error("open(%s) failed: %m", mount->path);
}
#endif
- return TRUE;
-}
-
-static void
-fs_quota_remove_storage(struct quota_root *root __attr_unused__,
- struct mail_storage *storage __attr_unused__)
-{
}
static const char *const *
@@ -200,8 +207,6 @@
root->uid, (caddr_t)&xdqblk) < 0) {
i_error("quotactl(Q_XGETQUOTA, %s) failed: %m",
root->mount->device_path);
- quota_set_error(_root->setup->quota,
- "Internal quota error");
return -1;
}
@@ -217,8 +222,6 @@
root->uid, (caddr_t)&dqblk) < 0) {
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
root->mount->device_path);
- quota_set_error(_root->setup->quota,
- "Internal quota error");
return -1;
}
@@ -255,32 +258,10 @@
return 1;
}
-static int
-fs_quota_set_resource(struct quota_root *root,
- const char *name __attr_unused__,
- uint64_t value __attr_unused__)
-{
- quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
- return -1;
-}
-
-static struct quota_root_transaction_context *
-fs_quota_transaction_begin(struct quota_root *root,
- struct quota_transaction_context *ctx)
-{
- struct quota_root_transaction_context *root_ctx;
-
- root_ctx = i_new(struct quota_root_transaction_context, 1);
- root_ctx->root = root;
- root_ctx->ctx = ctx;
- root_ctx->disabled = TRUE;
- return root_ctx;
-}
-
-static int
-fs_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+static int
+fs_quota_update(struct quota_root *root __attr_unused__,
+ struct quota_transaction_context *ctx __attr_unused__)
{
- i_free(ctx);
return 0;
}
@@ -288,26 +269,15 @@
"fs",
{
- fs_quota_init,
+ fs_quota_alloc,
+ NULL,
fs_quota_deinit,
- fs_quota_add_storage,
- fs_quota_remove_storage,
+ fs_quota_storage_added,
fs_quota_root_get_resources,
-
fs_quota_get_resource,
- fs_quota_set_resource,
-
- fs_quota_transaction_begin,
- fs_quota_transaction_commit,
- quota_default_transaction_rollback,
-
- quota_default_try_alloc,
- quota_default_try_alloc_bytes,
- quota_default_test_alloc_bytes,
- quota_default_alloc,
- quota_default_free
+ fs_quota_update
}
};
Index: quota-maildir.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-maildir.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- quota-maildir.c 24 Jul 2006 01:57:15 -0000 1.12
+++ quota-maildir.c 30 Jul 2006 17:58:43 -0000 1.13
@@ -22,6 +22,7 @@
struct maildir_quota_root {
struct quota_root root;
+ const char *maildirsize_path;
uint64_t message_bytes_limit;
uint64_t message_count_limit;
@@ -29,6 +30,7 @@
uint64_t total_count;
int fd;
+ time_t recalc_last_stamp;
unsigned int master_message_limits:1;
};
@@ -56,8 +58,8 @@
MEMBER(use_excl_lock) FALSE
};
-static int maildir_sum_dir(struct mail_storage *storage, const char *dir,
- uint64_t *total_bytes, uint64_t *total_count)
+static int maildir_sum_dir(const char *dir, uint64_t *total_bytes,
+ uint64_t *total_count)
{
DIR *dirp;
struct dirent *dp;
@@ -71,8 +73,7 @@
if (dirp == NULL) {
if (errno == ENOENT || errno == ESTALE)
return 0;
- mail_storage_set_critical(storage, "opendir(%s) failed: %m",
- dir);
+ i_error("opendir(%s) failed: %m", dir);
return -1;
}
@@ -111,16 +112,14 @@
*total_bytes += st.st_size;
*total_count += 1;
} else if (errno != ENOENT && errno != ESTALE) {
- mail_storage_set_critical(storage,
- "stat(%s) failed: %m", str_c(path));
+ i_error("stat(%s) failed: %m", str_c(path));
ret = -1;
}
}
}
if (closedir(dirp) < 0) {
- mail_storage_set_critical(storage, "closedir(%s) failed: %m",
- dir);
+ i_error("closedir(%s) failed: %m", dir);
return -1;
}
return ret;
@@ -170,8 +169,7 @@
/* ignore if the directory got lost, stale or if it was
actually a file and not a directory */
if (errno != ENOENT && errno != ESTALE && errno != ENOTDIR) {
- mail_storage_set_critical(ctx->ctx->storage,
- "stat(%s) failed: %m", str_c(ctx->path));
+ i_error("stat(%s) failed: %m", str_c(ctx->path));
ctx->state = 0;
}
}
@@ -209,8 +207,7 @@
return ret;
}
-static int maildirsize_write(struct maildir_quota_root *root,
- struct mail_storage *storage, const char *path)
+static int maildirsize_write(struct maildir_quota_root *root, const char *path)
{
struct dotlock *dotlock;
string_t *str;
@@ -226,8 +223,7 @@
return -1;
}
- mail_storage_set_critical(storage,
- "file_dotlock_open(%s) failed: %m", path);
+ i_error("file_dotlock_open(%s) failed: %m", path);
return -1;
}
@@ -246,8 +242,7 @@
(unsigned long long)root->total_bytes,
(unsigned long long)root->total_count);
if (write_full(fd, str_data(str), str_len(str)) < 0) {
- mail_storage_set_critical(storage,
- "write_full(%s) failed: %m", path);
+ i_error("write_full(%s) failed: %m", path);
file_dotlock_delete(&dotlock);
return -1;
}
@@ -255,38 +250,34 @@
/* keep the fd open since we might want to update it later */
if (file_dotlock_replace(&dotlock,
DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
- mail_storage_set_critical(storage,
- "file_dotlock_replace(%s) failed: %m", path);
+ i_error("file_dotlock_replace(%s) failed: %m", path);
return -1;
}
root->fd = fd;
return 0;
}
-static const char *maildirsize_get_path(struct mail_storage *storage)
+static void maildirsize_recalculate_init(struct maildir_quota_root *root)
{
- return t_strconcat(mail_storage_get_mailbox_control_dir(storage, ""),
- "/"MAILDIRSIZE_FILENAME, NULL);
+ root->total_bytes = root->total_count = 0;
+ root->recalc_last_stamp = 0;
}
-static int maildirsize_recalculate(struct maildir_quota_root *root,
- struct mail_storage *storage)
+static int maildirsize_recalculate_storage(struct maildir_quota_root *root,
+ struct mail_storage *storage)
{
struct maildir_list_context *ctx;
- const char *dir, *path;
- time_t mtime, last_stamp = 0;
+ const char *dir;
+ time_t mtime;
int ret = 0;
- root->total_bytes = root->total_count = 0;
-
ctx = maildir_list_init(storage);
while ((dir = maildir_list_next(ctx, &mtime)) != NULL) {
- if (mtime > last_stamp)
- last_stamp = mtime;
+ if (mtime > root->recalc_last_stamp)
+ root->recalc_last_stamp = mtime;
t_push();
- if (maildir_sum_dir(storage, dir,
- &root->total_bytes,
+ if (maildir_sum_dir(dir, &root->total_bytes,
&root->total_count) < 0)
ret = -1;
t_pop();
@@ -294,27 +285,58 @@
if (maildir_list_deinit(ctx) < 0)
ret = -1;
- if (ret == 0)
- ret = maildirs_check_have_changed(storage, last_stamp);
+ return ret;
+}
- t_push();
- path = maildirsize_get_path(storage);
+static int maildirsize_recalculate_finish(struct maildir_quota_root *root,
+ int ret)
+{
if (ret == 0) {
/* maildir didn't change, we can write the maildirsize file */
- ret = maildirsize_write(root, storage, path);
+ ret = maildirsize_write(root, root->maildirsize_path);
}
if (ret != 0) {
/* make sure it gets rebuilt later */
- if (unlink(path) < 0 && errno != ENOENT && errno != ESTALE) {
- mail_storage_set_critical(storage,
- "unlink(%s) failed: %m", path);
+ if (unlink(root->maildirsize_path) < 0 &&
+ errno != ENOENT && errno != ESTALE) {
+ i_error("unlink(%s) failed: %m",
+ root->maildirsize_path);
}
}
- t_pop();
return ret;
}
+static int maildirsize_recalculate(struct maildir_quota_root *root)
+{
+ struct mail_storage *const *storages;
+ unsigned int i, count;
+ int ret = 0;
+
+ maildirsize_recalculate_init(root);
+
+ /* count mails from all storages */
+ storages = array_get(&root->root.quota->storages, &count);
+ for (i = 0; i < count; i++) {
+ if (maildirsize_recalculate_storage(root, storages[i]) < 0) {
+ ret = -1;
+ break;
+ }
+ }
+
+ if (ret == 0) {
+ /* check if any of the directories have changed */
+ for (i = 0; i < count; i++) {
+ ret = maildirs_check_have_changed(storages[i],
+ root->recalc_last_stamp);
+ if (ret != 0)
+ break;
+ }
+ }
+
+ return maildirsize_recalculate_finish(root, ret);
+}
+
static int maildirsize_parse(struct maildir_quota_root *root,
int fd, const char *const *lines)
{
@@ -397,32 +419,26 @@
return 1;
}
-static int maildirsize_read(struct maildir_quota_root *root,
- struct mail_storage *storage)
+static int maildirsize_read(struct maildir_quota_root *root)
{
- const char *path;
char buf[5120+1];
unsigned int size;
int fd, ret;
t_push();
- path = maildirsize_get_path(storage);
if (root->fd != -1) {
- if (close(root->fd) < 0) {
- mail_storage_set_critical(storage,
- "close(%s) failed: %m", path);
- }
+ if (close(root->fd) < 0)
+ i_error("close(%s) failed: %m", root->maildirsize_path);
root->fd = -1;
}
- fd = nfs_safe_open(path, O_RDWR | O_APPEND);
+ fd = nfs_safe_open(root->maildirsize_path, O_RDWR | O_APPEND);
if (fd == -1) {
if (errno == ENOENT)
ret = 0;
else {
ret = -1;
- mail_storage_set_critical(storage,
- "open(%s) failed: %m", path);
+ i_error("open(%s) failed: %m", root->maildirsize_path);
}
t_pop();
return ret;
@@ -435,8 +451,7 @@
if (ret < 0) {
if (errno == ESTALE)
break;
- mail_storage_set_critical(storage, "read(%s) failed: %m",
- path);
+ i_error("read(%s) failed: %m", root->maildirsize_path);
}
size += ret;
}
@@ -467,12 +482,11 @@
return ret;
}
-static int maildirquota_refresh(struct maildir_quota_root *root,
- struct mail_storage *storage)
+static int maildirquota_refresh(struct maildir_quota_root *root)
{
int ret;
- ret = maildirsize_read(root, storage);
+ ret = maildirsize_read(root);
if (ret == 0) {
if (root->message_bytes_limit == (uint64_t)-1 &&
root->message_count_limit == (uint64_t)-1) {
@@ -480,13 +494,12 @@
return 0;
}
- ret = maildirsize_recalculate(root, storage);
+ ret = maildirsize_recalculate(root);
}
return ret < 0 ? -1 : 0;
}
static int maildirsize_update(struct maildir_quota_root *root,
- struct mail_storage *storage,
int count_diff, int64_t bytes_diff)
{
const char *str;
@@ -508,47 +521,22 @@
if (errno == ESTALE) {
/* deleted/replaced already, ignore */
} else {
- mail_storage_set_critical(storage,
- "write_full(%s) failed: %m",
- maildirsize_get_path(storage));
+ i_error("write_full(%s) failed: %m",
+ root->maildirsize_path);
}
}
t_pop();
return ret;
}
-static struct quota_root *
-maildir_quota_init(struct quota_setup *setup, const char *name __attr_unused__)
+static struct quota_root *maildir_quota_alloc(void)
{
struct maildir_quota_root *root;
- const char *const *args;
- unsigned long long size;
root = i_new(struct maildir_quota_root, 1);
- root->root.name = i_strdup(name);
- root->root.v = quota_backend_maildir.v;
root->fd = -1;
root->message_bytes_limit = (uint64_t)-1;
root->message_count_limit = (uint64_t)-1;
-
- t_push();
- args = t_strsplit(setup->data, ":");
-
- for (; *args != '\0'; args++) {
- if (strncmp(*args, "storage=", 8) == 0) {
- size = strtoull(*args + 8, NULL, 10) * 1024;
- if (size != 0)
- root->message_bytes_limit = size;
- root->master_message_limits = TRUE;
- } else if (strncmp(*args, "messages=", 9) == 0) {
- size = strtoull(*args + 9, NULL, 10);
- if (size != 0)
- root->message_count_limit = size;
- root->master_message_limits = TRUE;
- }
- }
- t_pop();
-
return &root->root;
}
@@ -556,28 +544,45 @@
{
struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
- i_free(root->root.name);
i_free(root);
}
-static bool
-maildir_quota_add_storage(struct quota_root *root __attr_unused__,
- struct mail_storage *_storage)
+static void
+maildir_quota_root_storage_added(struct quota_root *_root,
+ struct mail_storage *storage)
{
- if (strcmp(_storage->name, "maildir") == 0) {
- struct maildir_storage *storage =
- (struct maildir_storage *)_storage;
+ struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
+ const char *control_dir;
- /* For newly generated filenames add ,S=size. */
- storage->save_size_in_filename = TRUE;
- }
- return TRUE;
+ if (root->maildirsize_path != NULL)
+ return;
+
+ control_dir = mail_storage_get_mailbox_control_dir(storage, "");
+ root->maildirsize_path =
+ p_strconcat(_root->pool, control_dir,
+ "/"MAILDIRSIZE_FILENAME, NULL);
}
static void
-maildir_quota_remove_storage(struct quota_root *root __attr_unused__,
- struct mail_storage *storage __attr_unused__)
+maildir_quota_storage_added(struct quota *quota,
+ struct mail_storage *_storage)
{
+ struct maildir_storage *storage =
+ (struct maildir_storage *)_storage;
+ struct quota_root **roots;
+ unsigned int i, count;
+
+ if (strcmp(_storage->name, "maildir") != 0)
+ return;
+
+ roots = array_get_modifiable("a->roots, &count);
+ for (i = 0; i < count; i++) {
+ if (roots[i]->backend == "a_backend_maildir)
+ maildir_quota_root_storage_added(roots[i], _storage);
+ }
+
+ /* For newly generated filenames add ,S=size. */
+ storage->save_size_in_filename = TRUE;
}
static const char *const *
@@ -592,120 +597,49 @@
return resources_both;
}
-static struct mail_storage *
-maildir_quota_root_get_storage(struct quota_root *root)
-{
- /* FIXME: figure out how to support multiple storages */
- struct mail_storage *const *storages;
- unsigned int count;
-
- storages = array_get(&root->storages, &count);
- i_assert(count > 0);
-
- return storages[0];
-}
-
static int
maildir_quota_get_resource(struct quota_root *_root, const char *name,
- uint64_t *value_r, uint64_t *limit_r)
+ uint64_t *value_r, uint64_t *limit __attr_unused__)
{
struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
- if (maildirquota_refresh(root,
- maildir_quota_root_get_storage(_root)) < 0)
+ if (maildirquota_refresh(root) < 0)
return -1;
- if (strcmp(name, QUOTA_NAME_STORAGE) == 0) {
- if (root->message_bytes_limit == (uint64_t)-1)
- return 0;
-
- *limit_r = root->message_bytes_limit / 1024;
+ if (strcmp(name, QUOTA_NAME_STORAGE) == 0)
*value_r = root->total_bytes / 1024;
- } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
- if (root->message_count_limit == (uint64_t)-1)
- return 0;
-
- *limit_r = root->message_count_limit;
+ else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
*value_r = root->total_count;
- } else {
+ else
return 0;
- }
return 1;
}
static int
-maildir_quota_set_resource(struct quota_root *root,
- const char *name __attr_unused__,
- uint64_t value __attr_unused__)
-{
- quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
- return -1;
-}
-
-static struct quota_root_transaction_context *
-maildir_quota_transaction_begin(struct quota_root *_root,
- struct quota_transaction_context *_ctx)
-{
- struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
- struct quota_root_transaction_context *ctx;
-
- ctx = i_new(struct quota_root_transaction_context, 1);
- ctx->root = _root;
- ctx->ctx = _ctx;
-
- if (maildirquota_refresh(root,
- maildir_quota_root_get_storage(_root)) < 0) {
- /* failed calculating the current quota */
- ctx->bytes_current = (uint64_t)-1;
- } else {
- ctx->bytes_limit = root->message_bytes_limit;
- ctx->count_limit = root->message_count_limit;
- ctx->bytes_current = root->total_bytes;
- ctx->count_current = root->total_count;
- }
- return ctx;
-}
-
-static int
-maildir_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+maildir_quota_update(struct quota_root *_root,
+ struct quota_transaction_context *ctx)
{
struct maildir_quota_root *root =
- (struct maildir_quota_root *)ctx->root;
- int ret = ctx->bytes_current == (uint64_t)-1 ? -1 : 0;
+ (struct maildir_quota_root *) _root;
- if (root->fd != -1 && ret == 0) {
+ if (root->fd != -1) {
/* if writing fails, we don't care all that much */
- (void)maildirsize_update(root,
- maildir_quota_root_get_storage(ctx->root),
- ctx->count_diff, ctx->bytes_diff);
+ (void)maildirsize_update(root, ctx->count_used,
+ ctx->bytes_used);
}
- i_free(ctx);
- return ret;
+ return 0;
}
struct quota_backend quota_backend_maildir = {
"maildir",
{
- maildir_quota_init,
+ maildir_quota_alloc,
+ NULL,
maildir_quota_deinit,
-
- maildir_quota_add_storage,
- maildir_quota_remove_storage,
-
+ maildir_quota_storage_added,
maildir_quota_root_get_resources,
-
maildir_quota_get_resource,
- maildir_quota_set_resource,
-
- maildir_quota_transaction_begin,
- maildir_quota_transaction_commit,
- quota_default_transaction_rollback,
-
- quota_default_try_alloc,
- quota_default_try_alloc_bytes,
- quota_default_test_alloc_bytes,
- quota_default_alloc,
- quota_default_free
+ maildir_quota_update
}
};
Index: quota-plugin.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-plugin.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- quota-plugin.c 1 Mar 2006 09:15:03 -0000 1.3
+++ quota-plugin.c 30 Jul 2006 17:58:43 -0000 1.4
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 Timo Sirainen */
+/* Copyright (C) 2005 Timo Sirainen & Tianyan Liu */
#include "lib.h"
#include "mail-storage.h"
@@ -14,20 +14,68 @@
struct quota *quota;
+static void quota_root_add_rules(const char *root_name,
+ struct quota_root *root)
+{
+ const char *rule_name, *rule, *error;
+ unsigned int i;
+
+ t_push();
+
+ rule_name = t_strconcat(root_name, "_RULE", NULL);
+ for (i = 2;; i++) {
+ rule = getenv(rule_name);
+
+ if (rule == NULL)
+ break;
+
+ if (quota_root_add_rule(root, rule, &error) < 0) {
+ i_fatal("Quota root %s: Invalid rule: %s",
+ root_name, rule);
+ }
+ rule_name = t_strdup_printf("%s_RULE%d", root_name, i);
+ }
+
+ t_pop();
+}
+
void quota_plugin_init(void)
{
+ struct quota_root *root;
+ unsigned int i;
const char *env;
env = getenv("QUOTA");
- if (env != NULL) {
- quota = quota_init();
- /* Currently we support only one quota setup */
- (void)quota_setup_init(quota, env, TRUE);
+ if (env == NULL)
+ return;
- quota_next_hook_mail_storage_created =
- hook_mail_storage_created;
- hook_mail_storage_created = quota_mail_storage_created;
+ quota = quota_init();
+
+ root = quota_root_init(quota, env);
+ if (root == NULL)
+ i_fatal("Couldn't create quota root: %s", env);
+ quota_root_add_rules("QUOTA", root);
+
+ t_push();
+ for (i = 2;; i++) {
+ const char *root_name;
+
+ root_name = t_strdup_printf("QUOTA%d", i);
+ env = getenv(root_name);
+
+ if (env == NULL)
+ break;
+
+ root = quota_root_init(quota, env);
+ if (root == NULL)
+ i_fatal("Couldn't create quota root: %s", env);
+ quota_root_add_rules(root_name, root);
}
+ t_pop();
+
+ quota_next_hook_mail_storage_created =
+ hook_mail_storage_created;
+ hook_mail_storage_created = quota_mail_storage_created;
}
void quota_plugin_deinit(void)
Index: quota-private.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-private.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- quota-private.h 28 Jun 2006 13:11:00 -0000 1.10
+++ quota-private.h 30 Jul 2006 17:58:43 -0000 1.11
@@ -9,54 +9,27 @@
extern unsigned int quota_module_id;
struct quota {
- ARRAY_DEFINE(setups, struct quota_setup *);
- char *last_error;
-};
-
-struct quota_setup {
- struct quota *quota;
-
- struct quota_backend *backend;
- char *data;
-
- /* List of quota roots. It's array because there shouldn't be many. */
ARRAY_DEFINE(roots, struct quota_root *);
-
- unsigned int user_root:1;
+ ARRAY_DEFINE(storages, struct mail_storage *);
};
struct quota_backend_vfuncs {
- struct quota_root *(*init)(struct quota_setup *setup, const char *name);
+ struct quota_root *(*alloc)(void);
+ int (*init)(struct quota_root *root, const char *args);
void (*deinit)(struct quota_root *root);
- bool (*add_storage)(struct quota_root *root,
- struct mail_storage *storage);
- void (*remove_storage)(struct quota_root *root,
- struct mail_storage *storage);
+ /* called once for each backend */
+ void (*storage_added)(struct quota *quota,
+ struct mail_storage *storage);
const char *const *(*get_resources)(struct quota_root *root);
+ /* the limit is set by default, so it shouldn't normally need to
+ be changed. */
int (*get_resource)(struct quota_root *root, const char *name,
- uint64_t *value_r, uint64_t *limit_r);
- int (*set_resource)(struct quota_root *root,
- const char *name, uint64_t value);
-
- struct quota_root_transaction_context *
- (*transaction_begin)(struct quota_root *root,
- struct quota_transaction_context *ctx);
- int (*transaction_commit)(struct quota_root_transaction_context *ctx);
- void (*transaction_rollback)
- (struct quota_root_transaction_context *ctx);
+ uint64_t *value_r, uint64_t *limit);
- int (*try_alloc)(struct quota_root_transaction_context *ctx,
- struct mail *mail, bool *too_large_r);
- int (*try_alloc_bytes)(struct quota_root_transaction_context *ctx,
- uoff_t size, bool *too_large_r);
- int (*test_alloc_bytes)(struct quota_root_transaction_context *ctx,
- uoff_t size, bool *too_large_r);
- void (*alloc)(struct quota_root_transaction_context *ctx,
- struct mail *mail);
- void (*free)(struct quota_root_transaction_context *ctx,
- struct mail *mail);
+ int (*update)(struct quota_root *root,
+ struct quota_transaction_context *ctx);
};
struct quota_backend {
@@ -64,72 +37,46 @@
struct quota_backend_vfuncs v;
};
+struct quota_rule {
+ char *mailbox_name;
+
+ int64_t bytes_limit, count_limit;
+};
+
struct quota_root {
- struct quota_setup *setup;
+ pool_t pool;
/* Unique quota root name. */
char *name;
- struct quota_backend_vfuncs v;
+ /* pointer to the quota that owns this root */
+ struct quota *quota;
+
+ struct quota_backend *backend;
+ struct quota_rule default_rule;
+ ARRAY_DEFINE(rules, struct quota_rule);
- /* Mail storages using this quota root. */
- ARRAY_DEFINE(storages, struct mail_storage *);
/* Module-specific contexts. See quota_module_id. */
ARRAY_DEFINE(quota_module_contexts, void);
-
- unsigned int user_root:1;
-};
-
-struct quota_root_iter {
- struct quota_mail_storage *qstorage;
- unsigned int idx;
};
struct quota_transaction_context {
struct quota *quota;
+ struct mailbox *box;
- ARRAY_DEFINE(root_transactions,
- struct quota_root_transaction_context *);
- struct mail *mail;
-};
-
-struct quota_root_transaction_context {
- struct quota_root *root;
- struct quota_transaction_context *ctx;
-
- int count_diff;
- int64_t bytes_diff;
+ int64_t bytes_used, count_used;
+ uint64_t bytes_left, count_left;
- uint64_t bytes_limit, count_limit;
- uint64_t bytes_current, count_current;
+ struct mail *tmp_mail;
- unsigned int disabled:1;
+ unsigned int failed:1;
};
/* Register storage to all user's quota roots. */
void quota_add_user_storage(struct quota *quota, struct mail_storage *storage);
-
-/* Likn root and storage together. Returns TRUE if successful, FALSE if it
- can't be done (eg. different filesystems with filesystem quota) */
-bool quota_mail_storage_add_root(struct mail_storage *storage,
- struct quota_root *root);
-void quota_mail_storage_remove_root(struct mail_storage *storage,
- struct quota_root *root);
+void quota_remove_user_storage(struct quota *quota,
+ struct mail_storage *storage);
void quota_set_error(struct quota *quota, const char *errormsg);
-/* default simple implementations for bytes/count updating */
-void
-quota_default_transaction_rollback(struct quota_root_transaction_context *ctx);
-int quota_default_try_alloc(struct quota_root_transaction_context *ctx,
- struct mail *mail, bool *too_large_r);
-int quota_default_try_alloc_bytes(struct quota_root_transaction_context *ctx,
- uoff_t size, bool *too_large_r);
-int quota_default_test_alloc_bytes(struct quota_root_transaction_context *ctx,
- uoff_t size, bool *too_large_r);
-void quota_default_alloc(struct quota_root_transaction_context *ctx,
- struct mail *mail);
-void quota_default_free(struct quota_root_transaction_context *ctx,
- struct mail *mail);
-
#endif
Index: quota-storage.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-storage.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- quota-storage.c 28 Jun 2006 13:11:00 -0000 1.10
+++ quota-storage.c 30 Jul 2006 17:58:43 -0000 1.11
@@ -16,10 +16,6 @@
struct quota_mail_storage {
struct mail_storage_vfuncs super;
- struct quota *quota;
-
- /* List of quota roots this storage belongs to. */
- ARRAY_DEFINE(roots, struct quota_root *);
};
struct quota_mailbox {
@@ -58,7 +54,7 @@
struct quota_transaction_context *qt;
t = qbox->super.transaction_begin(box, flags);
- qt = quota_transaction_begin(box);
+ qt = quota_transaction_begin(quota, box);
array_idx_set(&t->module_contexts, quota_storage_module_id, &qt);
return t;
@@ -76,8 +72,8 @@
return -1;
} else {
(void)quota_transaction_commit(qt);
- if (qt->mail != NULL)
- mail_free(&qt->mail);
+ if (qt->tmp_mail != NULL)
+ mail_free(&qt->tmp_mail);
return 0;
}
}
@@ -90,8 +86,8 @@
qbox->super.transaction_rollback(ctx);
- if (qt->mail != NULL)
- mail_free(&qt->mail);
+ if (qt->tmp_mail != NULL)
+ mail_free(&qt->tmp_mail);
quota_transaction_rollback(qt);
}
@@ -129,8 +125,8 @@
mail_storage_set_error(t->box->storage, "Quota exceeded");
return -1;
} else {
- mail_storage_set_error(t->box->storage, "%s",
- quota_last_error(quota));
+ mail_storage_set_error(t->box->storage,
+ "Internal quota calculation error");
return -1;
}
}
@@ -145,11 +141,11 @@
if (dest_mail == NULL) {
/* we always want to know the mail size */
- if (qt->mail == NULL) {
- qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
- NULL);
+ if (qt->tmp_mail == NULL) {
+ qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
+ NULL);
}
- dest_mail = qt->mail;
+ dest_mail = qt->tmp_mail;
}
qbox->save_hack = FALSE;
@@ -186,25 +182,25 @@
full mail. */
bool too_large;
- ret = quota_test_alloc_bytes(qt, st->st_size, &too_large);
+ ret = quota_test_alloc(qt, st->st_size, &too_large);
if (ret == 0) {
mail_storage_set_error(t->box->storage,
"Quota exceeded");
return -1;
} else if (ret < 0) {
- mail_storage_set_error(t->box->storage, "%s",
- quota_last_error(quota));
+ mail_storage_set_error(t->box->storage,
+ "Internal quota calculation error");
return -1;
}
}
if (dest_mail == NULL) {
/* we always want to know the mail size */
- if (qt->mail == NULL) {
- qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
- NULL);
+ if (qt->tmp_mail == NULL) {
+ qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
+ NULL);
}
- dest_mail = qt->mail;
+ dest_mail = qt->tmp_mail;
}
return qbox->super.save_init(t, flags, keywords, received_date,
@@ -214,13 +210,15 @@
static int quota_save_finish(struct mail_save_context *ctx)
{
+ struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx->transaction);
struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
if (qbox->super.save_finish(ctx) < 0)
return -1;
qbox->save_hack = TRUE;
- return quota_check(ctx->transaction, ctx->dest_mail);
+ return quota_check(ctx->transaction, ctx->dest_mail != NULL ?
+ ctx->dest_mail : qt->tmp_mail);
}
static struct mailbox *
@@ -295,22 +293,8 @@
static void quota_storage_destroy(struct mail_storage *storage)
{
struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
- struct quota_root *const *roots;
- struct mail_storage *const *storages;
- unsigned int i, j, root_count, storage_count;
- /* remove the storage from all roots' storages list */
- roots = array_get(&qstorage->roots, &root_count);
- for (i = 0; i < root_count; i++) {
- storages = array_get(&roots[i]->storages, &storage_count);
- for (j = 0; j < storage_count; j++) {
- if (storages[j] == storage) {
- array_delete(&roots[i]->storages, j, 1);
- break;
- }
- }
- i_assert(j != storage_count);
- }
+ quota_remove_user_storage(quota, storage);
qstorage->super.destroy(storage);
}
@@ -328,8 +312,6 @@
storage->v.mailbox_open = quota_mailbox_open;
storage->v.mailbox_delete = quota_mailbox_delete;
- ARRAY_CREATE(&qstorage->roots, storage->pool, struct quota_root *, 4);
-
if (!quota_storage_module_id_set) {
quota_storage_module_id = mail_storage_module_id++;
quota_storage_module_id_set = TRUE;
@@ -343,74 +325,3 @@
quota_add_user_storage(quota, storage);
}
}
-
-bool quota_mail_storage_add_root(struct mail_storage *storage,
- struct quota_root *root)
-{
- struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
-
- if (!root->v.add_storage(root, storage))
- return FALSE;
-
- array_append(&root->storages, &storage, 1);
- array_append(&qstorage->roots, &root, 1);
- return TRUE;
-}
-
-void quota_mail_storage_remove_root(struct mail_storage *storage,
- struct quota_root *root)
-{
- struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
- struct mail_storage *const *storages;
- struct quota_root *const *roots;
- unsigned int i, count;
-
- storages = array_get(&root->storages, &count);
- for (i = 0; i < count; i++) {
- if (storages[i] == storage) {
- array_delete(&root->storages, i, 1);
- break;
- }
- }
- i_assert(i != count);
-
- roots = array_get(&qstorage->roots, &count);
- for (i = 0; i < count; i++) {
- if (roots[i] == root) {
- array_delete(&qstorage->roots, i, 1);
- break;
- }
- }
- i_assert(i != count);
-
- root->v.remove_storage(root, storage);
-}
-
-struct quota_root_iter *quota_root_iter_init(struct mailbox *box)
-{
- struct quota_mail_storage *qstorage = QUOTA_CONTEXT(box->storage);
- struct quota_root_iter *iter;
-
- iter = i_new(struct quota_root_iter, 1);
- iter->qstorage = qstorage;
- return iter;
-}
-
-struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
-{
- struct quota_root *const *roots;
- unsigned int count;
-
- roots = array_get(&iter->qstorage->roots, &count);
- i_assert(iter->idx <= count);
-
- if (iter->idx >= count)
- return NULL;
-
- return roots[iter->idx++];
-}
-
-void quota_root_iter_deinit(struct quota_root_iter *iter)
-{
- i_free(iter);
-}
Index: quota.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- quota.c 28 Jun 2006 13:11:00 -0000 1.11
+++ quota.c 30 Jul 2006 17:58:43 -0000 1.12
@@ -5,6 +5,16 @@
#include "hash.h"
#include "quota-private.h"
#include "quota-fs.h"
+#include <stdlib.h>
+
+#define RULE_NAME_ALL_MAILBOXES "*"
+
+struct quota_root_iter {
+ struct quota *quota;
+ struct mailbox *box;
+
+ unsigned int i;
+};
unsigned int quota_module_id = 0;
@@ -30,98 +40,92 @@
struct quota *quota;
quota = i_new(struct quota, 1);
- ARRAY_CREATE("a->setups, default_pool, struct quota_setup *, 4);
+ ARRAY_CREATE("a->roots, default_pool, struct quota_root *, 4);
+ ARRAY_CREATE("a->storages, default_pool, struct mail_storage *, 8);
+
return quota;
}
void quota_deinit(struct quota *quota)
{
- while (array_count("a->setups) > 0) {
- struct quota_setup *const *setup;
+ struct quota_root **root;
- setup = array_idx("a->setups, 0);
- quota_setup_deinit(*setup);
+ while (array_count("a->roots) > 0) {
+ root = array_idx_modifiable("a->roots, 0);
+ quota_root_deinit(*root);
}
- array_free("a->setups);
+ array_free("a->roots);
+ array_free("a->storages);
i_free(quota);
}
-struct quota_setup *
-quota_setup_init(struct quota *quota, const char *data, bool user_root)
+static struct quota_backend *quota_backend_find(const char *name)
{
- struct quota_setup *setup;
- const char *backend_name, *p;
unsigned int i;
- setup = i_new(struct quota_setup, 1);
- setup->quota = quota;
- setup->data = i_strdup(data);
- setup->user_root = user_root;
- ARRAY_CREATE(&setup->roots, default_pool, struct quota_root *, 4);
+ for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
+ if (strcmp(quota_backends[i]->name, name) == 0)
+ return quota_backends[i];
+ }
+
+ return NULL;
+}
+
+struct quota_root *quota_root_init(struct quota *quota, const char *root_def)
+{
+ struct quota_root *root;
+ struct quota_backend *backend;
+ const char *p, *args, *backend_name;
t_push();
- p = strchr(setup->data, ':');
+
+ /* <backend>[:<quota root name>[:<backend args>]] */
+ p = strchr(root_def, ':');
if (p == NULL) {
- backend_name = setup->data;
- data = "";
+ backend_name = root_def;
+ args = NULL;
} else {
- backend_name = t_strdup_until(setup->data, p);
- data = p+1;
- }
- for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
- if (strcmp(quota_backends[i]->name, backend_name) == 0) {
- setup->backend = quota_backends[i];
- break;
- }
+ backend_name = t_strdup_until(root_def, p);
+ args = p + 1;
}
- if (setup->backend == NULL)
+ backend = quota_backend_find(backend_name);
+ if (backend == NULL)
i_fatal("Unknown quota backend: %s", backend_name);
-
+
t_pop();
- array_append("a->setups, &setup, 1);
- return setup;
-}
-
-void quota_setup_deinit(struct quota_setup *setup)
-{
- struct quota_setup *const *setups;
- unsigned int i, count;
+ root = backend->v.alloc();
+ root->quota = quota;
+ root->backend = backend;
+ root->pool = pool_alloconly_create("quota root", 512);
- setups = array_get(&setup->quota->setups, &count);
- for (i = 0; i < count; i++) {
- if (setups[i] == setup) {
- array_delete(&setup->quota->setups, i, 1);
- break;
+ if (args != NULL) {
+ /* save root's name */
+ p = strchr(args, ':');
+ if (p == NULL) {
+ root->name = p_strdup(root->pool, args);
+ args = NULL;
+ } else {
+ root->name = p_strdup_until(root->pool, args, p);
+ args = p + 1;
}
- }
- i_assert(i != count);
-
- while (array_count(&setup->roots) > 0) {
- struct quota_root *const *root;
-
- root = array_idx(&setup->roots, 0);
- quota_root_deinit(*root);
+ } else {
+ root->name = "";
}
- array_free(&setup->roots);
- i_free(setup->data);
- i_free(setup);
-}
+ ARRAY_CREATE(&root->rules, default_pool, struct quota_rule, 4);
+ ARRAY_CREATE(&root->quota_module_contexts, default_pool, void *, 5);
-struct quota_root *
-quota_root_init(struct quota_setup *setup, const char *name)
-{
- struct quota_root *root;
+ array_append("a->roots, &root, 1);
- root = setup->backend->v.init(setup, name);
- root->setup = setup;
- ARRAY_CREATE(&root->storages, default_pool, struct mail_storage *, 8);
- array_create(&root->quota_module_contexts,
- default_pool, sizeof(void *), 5);
- array_append(&setup->roots, &root, 1);
+ if (backend->v.init != NULL) {
+ if (backend->v.init(root, args) < 0) {
+ quota_root_deinit(root);
+ return NULL;
+ }
+ }
if (hook_quota_root_created != NULL)
hook_quota_root_created(root);
@@ -130,341 +134,382 @@
void quota_root_deinit(struct quota_root *root)
{
- /* make a copy, since root is freed */
- struct array module_contexts = root->quota_module_contexts.arr;
- struct mail_storage *const *storage_p;
+ pool_t pool = root->pool;
struct quota_root *const *roots;
unsigned int i, count;
- /* remove from all storages */
- while (array_count(&root->storages) > 0) {
- storage_p = array_idx(&root->storages, 0);
- quota_mail_storage_remove_root(*storage_p, root);
+ roots = array_get(&root->quota->roots, &count);
+ for (i = 0; i < count; i++) {
+ if (roots[i] == root)
+ array_delete(&root->quota->roots, i, 1);
}
- /* remove from setup */
- roots = array_get(&root->setup->roots, &count);
+ array_free(&root->rules);
+ array_free(&root->quota_module_contexts);
+
+ root->backend->v.deinit(root);
+ pool_unref(pool);
+}
+
+static struct quota_rule *
+quota_root_rule_find(struct quota_root *root, const char *name)
+{
+ struct quota_rule *rules;
+ unsigned int i, count;
+
+ rules = array_get_modifiable(&root->rules, &count);
for (i = 0; i < count; i++) {
- if (roots[i] == root) {
- array_delete(&root->setup->roots, i, 1);
- break;
- }
+ if (strcmp(rules[i].mailbox_name, name) == 0)
+ return &rules[i];
}
- i_assert(i != count);
-
- array_free(&root->storages);
- root->v.deinit(root);
- _array_free(&module_contexts);
+ return NULL;
}
-void quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
+int quota_root_add_rule(struct quota_root *root, const char *rule_def,
+ const char **error_r)
{
- struct quota_setup *const *setups;
- struct quota_root *const *roots;
- unsigned int i, j, setup_count, root_count;
- bool found = FALSE;
+ struct quota_rule *rule;
+ const char **args;
+ int ret = 0;
- setups = array_get("a->setups, &setup_count);
- for (i = 0; i < setup_count; i++) {
- roots = array_get(&setups[i]->roots, &root_count);
- for (j = 0; j < root_count; j++) {
- if (!roots[j]->user_root)
- continue;
+ if (*rule_def == '\0') {
+ *error_r = "Empty rule";
+ return -1;
+ }
- if (quota_mail_storage_add_root(storage, roots[j]))
- found = TRUE;
+ /* <mailbox name>:<quota limits> */
+ t_push();
+ args = t_strsplit(rule_def, ":");
+
+ rule = quota_root_rule_find(root, *args);
+ if (rule == NULL) {
+ if (strcmp(*args, RULE_NAME_ALL_MAILBOXES) == 0)
+ rule = &root->default_rule;
+ else {
+ rule = array_append_space(&root->rules);
+ rule->mailbox_name = p_strdup(root->pool, *args);
}
}
- if (!found && setup_count > 0) {
- /* create a new quota root for the storage */
- struct quota_root *root;
+ for (args++; *args != NULL; args++) {
+ if (strncmp(*args, "storage=", 8) == 0)
+ rule->bytes_limit = strtoll(*args + 8, NULL, 10) * 1024;
+ else if (strncmp(*args, "messages=", 9) == 0)
+ rule->count_limit = strtoll(*args + 9, NULL, 10);
+ else {
+ *error_r = p_strdup_printf(root->pool,
+ "Invalid rule limit: %s", *args);
+ ret = -1;
+ break;
+ }
+ }
+ t_pop();
+ return ret;
+}
- root = quota_root_init(setups[0], ""); // FIXME: name?
- found = quota_mail_storage_add_root(storage, root);
- i_assert(found);
+static bool quota_root_get_rule_limits(struct quota_root *root,
+ const char *mailbox_name,
+ uint64_t *bytes_limit_r,
+ uint64_t *count_limit_r)
+{
+ struct quota_rule *rule;
+ int64_t bytes_limit, count_limit;
+ bool found;
+
+ bytes_limit = root->default_rule.bytes_limit;
+ count_limit = root->default_rule.count_limit;
+
+ /* if default rule limits are 0, this rule applies only to specific
+ mailboxes */
+ found = bytes_limit != 0 || count_limit != 0;
+
+ rule = quota_root_rule_find(root, mailbox_name);
+ if (rule != NULL) {
+ bytes_limit += rule->bytes_limit;
+ count_limit += rule->count_limit;
+ found = TRUE;
}
+
+ *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit;
+ *count_limit_r = count_limit <= 0 ? 0 : count_limit;
+ return found;
}
-struct quota_root *quota_root_lookup(struct quota *quota, const char *name)
+void quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
{
- struct quota_setup *const *setups;
struct quota_root *const *roots;
- unsigned int i, j, setup_count, root_count;
+ struct quota_backend **backends;
+ unsigned int i, j, count;
- setups = array_get("a->setups, &setup_count);
- for (i = 0; i < setup_count; i++) {
- roots = array_get(&setups[i]->roots, &root_count);
- for (j = 0; j < root_count; j++) {
- if (strcmp(roots[j]->name, name) == 0)
- return roots[j];
+ array_append("a->storages, &storage, 1);
+
+ roots = array_get("a->roots, &count);
+ /* @UNSAFE: get different backends into one array */
+ backends = t_new(struct quota_backend *, count + 1);
+ for (i = 0; i < count; i++) {
+ for (j = 0; backends[j] != NULL; j++) {
+ if (backends[j] == roots[i]->backend)
+ break;
}
+ if (backends[j] == NULL)
+ backends[j] = roots[i]->backend;
}
- return NULL;
-}
-const char *quota_root_get_name(struct quota_root *root)
-{
- return root->name;
+ for (i = 0; backends[i] != NULL; i++) {
+ if (backends[i]->v.storage_added != NULL)
+ backends[i]->v.storage_added(quota, storage);
+ }
}
-const char *const *quota_root_get_resources(struct quota_root *root)
+void quota_remove_user_storage(struct quota *quota,
+ struct mail_storage *storage)
{
- return root->v.get_resources(root);
+ struct mail_storage *const *storages;
+ unsigned int i, count;
+
+ storages = array_get("a->storages, &count);
+ for (i = 0; i < count; i++) {
+ if (storages[i] == storage) {
+ array_delete("a->storages, i, 1);
+ break;
+ }
+ }
}
-int quota_get_resource(struct quota_root *root, const char *name,
- uint64_t *value_r, uint64_t *limit_r)
+struct quota_root_iter *
+quota_root_iter_init(struct quota *quota, struct mailbox *box)
{
- return root->v.get_resource(root, name, value_r, limit_r);
-}
+ struct quota_root_iter *iter;
-int quota_set_resource(struct quota_root *root,
- const char *name, uint64_t value)
-{
- return root->v.set_resource(root, name, value);
+ iter = i_new(struct quota_root_iter, 1);
+ iter->quota = quota;
+ iter->box = box;
+ return iter;
}
-struct quota_transaction_context *quota_transaction_begin(struct mailbox *box)
+struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
{
- struct quota_transaction_context *ctx;
- struct quota_root_transaction_context *root_ctx;
- struct quota_root_iter *iter;
- struct quota_root *root;
+ struct quota_root *const *roots, *root = NULL;
+ unsigned int count;
+ uint64_t value, limit;
+ int ret;
- ctx = i_new(struct quota_transaction_context, 1);
- ARRAY_CREATE(&ctx->root_transactions, default_pool,
- struct quota_root_transaction_context *, 4);
+ roots = array_get(&iter->quota->roots, &count);
+ if (iter->i >= count)
+ return NULL;
- iter = quota_root_iter_init(box);
- while ((root = quota_root_iter_next(iter)) != NULL) {
- root_ctx = root->v.transaction_begin(root, ctx);
- array_append(&ctx->root_transactions, &root_ctx, 1);
+ for (; iter->i < count; iter->i++) {
+ ret = quota_get_resource(roots[iter->i], "",
+ QUOTA_NAME_STORAGE, &value, &limit);
+ if (ret == 0) {
+ ret = quota_get_resource(roots[iter->i], "",
+ QUOTA_NAME_MESSAGES,
+ &value, &limit);
+ }
+ if (ret > 0) {
+ root = roots[iter->i];
+ break;
+ }
}
- quota_root_iter_deinit(iter);
- return ctx;
+
+ iter->i++;
+ return root;
}
-static void quota_transaction_free(struct quota_transaction_context *ctx)
+void quota_root_iter_deinit(struct quota_root_iter *iter)
{
- array_free(&ctx->root_transactions);
- i_free(ctx);
+ i_free(iter);
}
-int quota_transaction_commit(struct quota_transaction_context *ctx)
+struct quota_root *quota_root_lookup(struct quota *quota, const char *name)
{
- struct quota_root_transaction_context *const *root_transactions;
+ struct quota_root *const *roots;
unsigned int i, count;
- int ret = 0;
- root_transactions = array_get(&ctx->root_transactions, &count);
+ roots = array_get("a->roots, &count);
for (i = 0; i < count; i++) {
- struct quota_root_transaction_context *t =
- root_transactions[i];
-
- if (t->root->v.transaction_commit(t) < 0)
- ret = -1;
+ if (strcmp(roots[i]->name, name) == 0)
+ return roots[i];
}
-
- quota_transaction_free(ctx);
- return ret;
+ return NULL;
}
-void quota_transaction_rollback(struct quota_transaction_context *ctx)
+const char *quota_root_get_name(struct quota_root *root)
{
- struct quota_root_transaction_context *const *root_transactions;
- unsigned int i, count;
-
- root_transactions = array_get(&ctx->root_transactions, &count);
- for (i = 0; i < count; i++) {
- struct quota_root_transaction_context *t =
- root_transactions[i];
-
- t->root->v.transaction_rollback(t);
- }
-
- quota_transaction_free(ctx);
+ return root->name;
}
-int quota_try_alloc(struct quota_transaction_context *ctx,
- struct mail *mail, bool *too_large_r)
+const char *const *quota_root_get_resources(struct quota_root *root)
{
- struct quota_root_transaction_context *const *root_transactions;
- unsigned int i, count;
- int ret = 1;
-
- root_transactions = array_get(&ctx->root_transactions, &count);
- for (i = 0; i < count; i++) {
- struct quota_root_transaction_context *t =
- root_transactions[i];
-
- ret = t->root->v.try_alloc(t, mail, too_large_r);
- if (ret <= 0)
- break;
- }
- return ret;
+ return root->backend->v.get_resources(root);
}
-int quota_try_alloc_bytes(struct quota_transaction_context *ctx,
- uoff_t size, bool *too_large_r)
+int quota_get_resource(struct quota_root *root, const char *mailbox_name,
+ const char *name, uint64_t *value_r, uint64_t *limit_r)
{
- struct quota_root_transaction_context *const *root_transactions;
- unsigned int i, count;
- int ret = 1;
+ uint64_t bytes_limit, count_limit;
+ int ret;
- root_transactions = array_get(&ctx->root_transactions, &count);
- for (i = 0; i < count; i++) {
- struct quota_root_transaction_context *t =
- root_transactions[i];
+ (void)quota_root_get_rule_limits(root, mailbox_name,
+ &bytes_limit, &count_limit);
+ if (strcmp(name, QUOTA_NAME_STORAGE) == 0)
+ *limit_r = bytes_limit;
+ else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
+ *limit_r = count_limit;
+ else
+ *limit_r = 0;
- ret = t->root->v.try_alloc_bytes(t, size, too_large_r);
- if (ret <= 0)
- break;
- }
- return ret;
+ ret = root->backend->v.get_resource(root, name, value_r, limit_r);
+ return ret <= 0 ? ret :
+ (*limit_r == 0 ? 0 : 1);
}
-int quota_test_alloc_bytes(struct quota_transaction_context *ctx,
- uoff_t size, bool *too_large_r)
+int quota_set_resource(struct quota_root *root __attr_unused__,
+ const char *name __attr_unused__,
+ uint64_t value __attr_unused__, const char **error_r)
{
- struct quota_root_transaction_context *const *root_transactions;
- unsigned int i, count;
- int ret = 1;
-
- root_transactions = array_get(&ctx->root_transactions, &count);
- for (i = 0; i < count; i++) {
- struct quota_root_transaction_context *t =
- root_transactions[i];
-
- ret = t->root->v.test_alloc_bytes(t, size, too_large_r);
- if (ret <= 0)
- break;
- }
- return ret;
+ /* the quota information comes from userdb (or even config file),
+ so there's really no way to support this until some major changes
+ are done */
+ *error_r = MAIL_STORAGE_ERR_NO_PERMISSION;
+ return -1;
}
-void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
+struct quota_transaction_context *quota_transaction_begin(struct quota *quota,
+ struct mailbox *box)
{
- struct quota_root_transaction_context *const *root_transactions;
+ struct quota_transaction_context *ctx;
+ struct quota_root *const *roots;
+ const char *mailbox_name;
unsigned int i, count;
+ uint64_t current, limit, left;
+ int ret;
- root_transactions = array_get(&ctx->root_transactions, &count);
- for (i = 0; i < count; i++) {
- struct quota_root_transaction_context *t =
- root_transactions[i];
+ mailbox_name = mailbox_get_name(box);
+
+ ctx = i_new(struct quota_transaction_context, 1);
+ ctx->quota = quota;
+ ctx->box = box;
+ ctx->bytes_left = (uint64_t)-1;
+ ctx->count_left = (uint64_t) -1;
- t->root->v.alloc(t, mail);
+ /* find the lowest quota limits from all roots and use them */
+ roots = array_get("a->roots, &count);
+ for (i = 0; i < count; i++) {
+ ret = quota_get_resource(roots[i], mailbox_name,
+ QUOTA_NAME_STORAGE, ¤t, &limit);
+ if (ret > 0) {
+ left = limit < current ? 0 : limit - current;
+ if (ctx->bytes_left > left)
+ ctx->bytes_left = left;
+ } else if (ret < 0) {
+ ctx->failed = TRUE;
+ break;
+ }
+
+ ret = quota_get_resource(roots[i], mailbox_name,
+ QUOTA_NAME_MESSAGES, ¤t, &limit);
+ if (ret > 0) {
+ left = limit < current ? 0 : limit - current;
+ if (ctx->count_left > left)
+ ctx->count_left = left;
+ } else if (ret < 0) {
+ ctx->failed = TRUE;
+ break;
+ }
}
+ return ctx;
}
-void quota_free(struct quota_transaction_context *ctx, struct mail *mail)
+int quota_transaction_commit(struct quota_transaction_context *ctx)
{
- struct quota_root_transaction_context *const *root_transactions;
+ struct quota_root *const *roots;
unsigned int i, count;
+ int ret = 0;
- root_transactions = array_get(&ctx->root_transactions, &count);
- for (i = 0; i < count; i++) {
- struct quota_root_transaction_context *t =
- root_transactions[i];
-
- t->root->v.free(t, mail);
+ if (ctx->failed)
+ ret = -1;
+ else {
+ roots = array_get(&ctx->quota->roots, &count);
+ for (i = 0; i < count; i++) {
+ if (roots[i]->backend->v.update(roots[i], ctx) < 0)
+ ret = -1;
+ }
}
-}
-const char *quota_last_error(struct quota *quota)
-{
- return quota->last_error != NULL ? quota->last_error : "Unknown error";
-}
-
-void quota_set_error(struct quota *quota, const char *errormsg)
-{
- i_free(quota->last_error);
- quota->last_error = i_strdup(errormsg);
+ i_free(ctx);
+ return ret;
}
-void
-quota_default_transaction_rollback(struct quota_root_transaction_context *ctx)
+void quota_transaction_rollback(struct quota_transaction_context *ctx)
{
i_free(ctx);
}
-int quota_default_try_alloc_bytes(struct quota_root_transaction_context *ctx,
- uoff_t size, bool *too_large_r)
+int quota_try_alloc(struct quota_transaction_context *ctx,
+ struct mail *mail, bool *too_large_r)
{
int ret;
- ret = quota_default_test_alloc_bytes(ctx, size, too_large_r);
- if (ret <= 0 || ctx->disabled)
+ ret = quota_test_alloc(ctx, mail_get_physical_size(mail), too_large_r);
+ if (ret <= 0)
return ret;
- ctx->count_diff++;
- ctx->bytes_diff += size;
+ quota_alloc(ctx, mail);
return 1;
}
-int quota_default_test_alloc_bytes(struct quota_root_transaction_context *ctx,
- uoff_t size, bool *too_large_r)
+int quota_test_alloc(struct quota_transaction_context *ctx,
+ uoff_t size, bool *too_large_r)
{
- if (ctx->disabled) {
- *too_large_r = FALSE;
- return 1;
- }
- if (ctx->bytes_current == (uint64_t)-1) {
- /* failure in transaction initialization */
- return -1;
- }
+ struct quota_root *const *roots;
+ unsigned int i, count;
- *too_large_r = size > ctx->bytes_limit;
+ *too_large_r = FALSE;
- if (ctx->bytes_current + ctx->bytes_diff + size > ctx->bytes_limit)
- return 0;
- if (ctx->count_current + ctx->count_diff + 1 > ctx->count_limit)
- return 0;
- return 1;
-}
+ if (ctx->failed)
+ return -1;
+ if (ctx->count_left != 0 && ctx->bytes_left >= ctx->bytes_used + size)
+ return 1;
-int quota_default_try_alloc(struct quota_root_transaction_context *ctx,
- struct mail *mail, bool *too_large_r)
-{
- uoff_t size;
+ roots = array_get(&ctx->quota->roots, &count);
+ for (i = 0; i < count; i++) {
+ uint64_t bytes_limit, count_limit;
- if (ctx->disabled)
- return 1;
+ if (!quota_root_get_rule_limits(roots[i],
+ mailbox_get_name(ctx->box),
+ &bytes_limit, &count_limit))
+ continue;
- size = mail_get_physical_size(mail);
- if (size == (uoff_t)-1) {
- mail_storage_set_critical(mail->box->storage,
- "Quota: Couldn't get new message's size");
- return -1;
+ /* if size is bigger than any limit, then
+ it is bigger than the lowest limit */
+ if (size > bytes_limit) {
+ *too_large_r = TRUE;
+ break;
+ }
}
- return quota_default_try_alloc_bytes(ctx, size, too_large_r);
+ return 0;
}
-void quota_default_alloc(struct quota_root_transaction_context *ctx,
- struct mail *mail)
+void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
{
uoff_t size;
- if (ctx->disabled)
- return;
-
size = mail_get_physical_size(mail);
if (size != (uoff_t)-1)
- ctx->bytes_diff += size;
- ctx->count_diff++;
+ ctx->bytes_used += size;
+
+ ctx->count_used++;
}
-void quota_default_free(struct quota_root_transaction_context *ctx,
- struct mail *mail)
+void quota_free(struct quota_transaction_context *ctx, struct mail *mail)
{
uoff_t size;
- if (ctx->disabled)
- return;
-
size = mail_get_physical_size(mail);
if (size != (uoff_t)-1)
- ctx->bytes_diff -= size;
- ctx->count_diff--;
+ ctx->bytes_used -= size;
+
+ ctx->count_used--;
}
Index: quota.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- quota.h 11 Jun 2006 17:39:04 -0000 1.5
+++ quota.h 30 Jul 2006 17:58:43 -0000 1.6
@@ -19,20 +19,17 @@
struct quota *quota_init(void);
void quota_deinit(struct quota *quota);
-/* Create a new quota setup under which quota roots are created.
- user_root is TRUE if this quota points to user's own mailboxes instead of
- shared mailboxes. */
-struct quota_setup *
-quota_setup_init(struct quota *quota, const char *data, bool user_root);
-void quota_setup_deinit(struct quota_setup *setup);
-
/* Create a new quota root. */
-struct quota_root *
-quota_root_init(struct quota_setup *setup, const char *name);
+struct quota_root *quota_root_init(struct quota *quota, const char *root_def);
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,
+ const char **error_r);
+
/* List all quota roots. Returned quota roots are freed by quota_deinit(). */
-struct quota_root_iter *quota_root_iter_init(struct mailbox *box);
+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);
@@ -45,14 +42,15 @@
const char *const *quota_root_get_resources(struct quota_root *root);
/* Returns 1 if quota value was found, 0 if not, -1 if error. */
-int quota_get_resource(struct quota_root *root,
+int quota_get_resource(struct quota_root *root, const char *mailbox_name,
const char *name, uint64_t *value_r, uint64_t *limit_r);
/* Returns 0 if OK, -1 if error (eg. permission denied, invalid name). */
-int quota_set_resource(struct quota_root *root,
- const char *name, uint64_t value);
+int quota_set_resource(struct quota_root *root, const char *name,
+ uint64_t value, const char **error_r);
/* Start a new quota transaction. */
-struct quota_transaction_context *quota_transaction_begin(struct mailbox *box);
+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);
/* Rollback quota transaction changes. */
@@ -63,16 +61,11 @@
too_large_r is set to TRUE. */
int quota_try_alloc(struct quota_transaction_context *ctx,
struct mail *mail, bool *too_large_r);
-int quota_try_alloc_bytes(struct quota_transaction_context *ctx,
- uoff_t size, bool *too_large_r);
-/* Like quota_try_alloc_bytes(), but don't actually update the quota. */
-int quota_test_alloc_bytes(struct quota_transaction_context *ctx,
- uoff_t size, bool *too_large_r);
+/* Like quota_try_alloc(), but don't actually allocate anything. */
+int quota_test_alloc(struct quota_transaction_context *ctx,
+ uoff_t size, bool *too_large_r);
/* 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);
-/* Returns the last error message. */
-const char *quota_last_error(struct quota *quota);
-
#endif
More information about the dovecot-cvs
mailing list