quota-maildir.c revision 9b61a6db87c026656f8d2ae214e4486b98a069c0
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "lib.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "array.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "ioloop.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "nfs-workarounds.h"
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen#include "safe-mkstemp.h"
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen#include "mkdir-parents.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "read-full.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "write-full.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "str.h"
677e22747b82bf15b339e31d1d0106d62bf806daTimo Sirainen#include "maildir-storage.h"
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen#include "mailbox-list-private.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "quota-private.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include <stdio.h>
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include <stdlib.h>
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include <dirent.h>
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include <sys/stat.h>
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#define MAILDIRSIZE_FILENAME "maildirsize"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#define MAILDIRSIZE_STALE_SECS (60*15)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstruct maildir_quota_root {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct quota_root root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *maildirsize_ns;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *maildirsize_path;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen uint64_t total_bytes;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen uint64_t total_count;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int fd;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen time_t recalc_last_stamp;
ed22384e18b005c6a518478c515e7bfdb9e110eeTimo Sirainen off_t last_size;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen unsigned int limits_initialized:1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen};
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstruct maildir_list_context {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mailbox_list *list;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen struct maildir_quota_root *root;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct mailbox_list_iterate_context *iter;
61e84692827b6a64912343f515c984853021483aTimo Sirainen const struct mailbox_info *info;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen string_t *path;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int state;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen};
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenextern struct quota_backend quota_backend_maildir;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
ab0d9eecd85f74acae18fe88529302e0776cc500Timo Sirainenstatic struct dotlock_settings dotlock_settings = {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .timeout = 0,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .stale_timeout = 30
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen};
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int maildir_sum_dir(const char *dir, uint64_t *total_bytes,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen uint64_t *total_count)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen DIR *dirp;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct dirent *dp;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen string_t *path;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen const char *p;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen size_t len;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen uoff_t num;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen dirp = opendir(dir);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (dirp == NULL) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (errno == ENOENT || errno == ESTALE)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("opendir(%s) failed: %m", dir);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen path = t_str_new(256);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_append(path, dir);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_append_c(path, '/');
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen len = str_len(path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen while ((dp = readdir(dirp)) != NULL) {
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen if (dp->d_name[0] == '.' &&
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen (dp->d_name[1] == '\0' || dp->d_name[1] == '.'))
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen continue;
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen p = strstr(dp->d_name, ",S=");
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen num = (uoff_t)-1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (p != NULL) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* ,S=nnnn[:,] */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen p += 3;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen for (num = 0; *p >= '0' && *p <= '9'; p++)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen num = num * 10 + (*p - '0');
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (*p != ':' && *p != '\0' && *p != ',') {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* not in expected format, fallback to stat() */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen num = (uoff_t)-1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen } else {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen *total_bytes += num;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen *total_count += 1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (num == (uoff_t)-1) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct stat st;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_truncate(path, len);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_append(path, dp->d_name);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (stat(str_c(path), &st) == 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen *total_bytes += st.st_size;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen *total_count += 1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen } else if (errno != ENOENT && errno != ESTALE) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("stat(%s) failed: %m", str_c(path));
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (closedir(dirp) < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("closedir(%s) failed: %m", dir);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic struct maildir_list_context *
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenmaildir_list_init(struct maildir_quota_root *root, struct mailbox_list *list)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_list_context *ctx;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx = i_new(struct maildir_list_context, 1);
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen ctx->root = root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx->path = str_new(default_pool, 512);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->list = list;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->iter = mailbox_list_iter_init(list, "*",
09b4a50740d34c3f951dc1f9df8edf8283ac532aTimo Sirainen MAILBOX_LIST_ITER_SKIP_ALIASES |
18ffea71d9beeec3cc1d400f751926ee72807f62Timo Sirainen MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ctx;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainenstatic bool maildir_set_next_path(struct maildir_list_context *ctx)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen{
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen T_BEGIN {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen const char *path, *storage_name;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen str_truncate(ctx->path, 0);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen storage_name = mailbox_list_get_storage_name(
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen ctx->info->ns->list, ctx->info->vname);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(ctx->list, storage_name,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen &path) > 0) {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen str_append(ctx->path, path);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen str_append(ctx->path, ctx->state == 0 ?
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen "/new" : "/cur");
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen }
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen } T_END;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen return str_len(ctx->path) > 0;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen}
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic const char *
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenmaildir_list_next(struct maildir_list_context *ctx, time_t *mtime_r)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen struct quota_rule *rule;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct stat st;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen for (;;) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (ctx->state == 0) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ctx->info = mailbox_list_iter_next(ctx->iter);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (ctx->info == NULL)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return NULL;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen rule = quota_root_rule_find(ctx->root->root.set,
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen ctx->info->vname);
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen if (rule != NULL && rule->ignore) {
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen /* mailbox not included in quota */
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen continue;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (!maildir_set_next_path(ctx)) {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen ctx->state = 0;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen continue;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (++ctx->state == 2)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx->state = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (stat(str_c(ctx->path), &st) == 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* ignore if the directory got lost, stale or if it was
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen actually a file and not a directory */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (errno != ENOENT && errno != ESTALE && errno != ENOTDIR) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("stat(%s) failed: %m", str_c(ctx->path));
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx->state = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
f037abbef2cc57979356127e3b2bb456351460f7Timo Sirainen *mtime_r = st.st_mtime;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return str_c(ctx->path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic int maildir_list_deinit(struct maildir_list_context *ctx)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen int ret = mailbox_list_iter_deinit(&ctx->iter);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_free(&ctx->path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen i_free(ctx);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic int
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainenmaildirs_check_have_changed(struct maildir_quota_root *root,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *ns, time_t latest_mtime)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_list_context *ctx;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen time_t mtime;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx = maildir_list_init(root, ns->list);
ccb77e2f63626ec46e5745ef4f38baa8e8e504fcTimo Sirainen while (maildir_list_next(ctx, &mtime) != NULL) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (mtime > latest_mtime) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = 1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (maildir_list_deinit(ctx) < 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int maildirsize_write(struct maildir_quota_root *root, const char *path)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const struct mail_storage_settings *set =
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen root->maildirsize_ns->mail_set;
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen struct quota_root *_root = &root->root;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *const *namespaces;
fea7b8b3fc182e415b1875d79587c0aa1adb09d7Timo Sirainen unsigned int i, count;
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen struct mailbox_permissions perm;
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen const char *p, *dir;
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen string_t *str, *temp_path;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int fd;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen i_assert(root->fd == -1);
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen
fea7b8b3fc182e415b1875d79587c0aa1adb09d7Timo Sirainen /* figure out what permissions we should use for maildirsize.
fea7b8b3fc182e415b1875d79587c0aa1adb09d7Timo Sirainen use the inbox namespace's permissions if possible. */
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen perm.file_create_mode = 0600; perm.dir_create_mode = 0700;
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen perm.file_create_gid = (gid_t)-1;
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen perm.file_create_gid_origin = "default";
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen namespaces = array_get(&root->root.quota->namespaces, &count);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_assert(count > 0);
fea7b8b3fc182e415b1875d79587c0aa1adb09d7Timo Sirainen for (i = 0; i < count; i++) {
17cf149e5f47183bfcc1503649dfb92a14f9dcd9Timo Sirainen if ((namespaces[i]->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
17cf149e5f47183bfcc1503649dfb92a14f9dcd9Timo Sirainen continue;
17cf149e5f47183bfcc1503649dfb92a14f9dcd9Timo Sirainen
17cf149e5f47183bfcc1503649dfb92a14f9dcd9Timo Sirainen mailbox_list_get_root_permissions(namespaces[i]->list,
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen &perm);
17cf149e5f47183bfcc1503649dfb92a14f9dcd9Timo Sirainen break;
fea7b8b3fc182e415b1875d79587c0aa1adb09d7Timo Sirainen }
fea7b8b3fc182e415b1875d79587c0aa1adb09d7Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen dotlock_settings.use_excl_lock = set->dotlock_use_excl;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen dotlock_settings.nfs_flush = set->mail_nfs_storage;
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen temp_path = t_str_new(128);
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen str_append(temp_path, path);
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen fd = safe_mkstemp_hostpid_group(temp_path, perm.file_create_mode,
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen perm.file_create_gid,
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen perm.file_create_gid_origin);
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen if (fd == -1 && errno == ENOENT) {
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen /* the control directory doesn't exist yet? create it */
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen p = strrchr(path, '/');
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen dir = t_strdup_until(path, p);
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen if (mkdir_parents_chgrp(dir, perm.dir_create_mode,
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen perm.file_create_gid,
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen perm.file_create_gid_origin) < 0 &&
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen errno != EEXIST) {
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen i_error("mkdir_parents(%s) failed: %m", dir);
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen return -1;
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen }
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen fd = safe_mkstemp_hostpid_group(temp_path,
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen perm.file_create_mode,
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen perm.file_create_gid,
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen perm.file_create_gid_origin);
cb07373a0afd4b0635e10a6757bd22e46dc9cf61Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (fd == -1) {
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen i_error("safe_mkstemp(%s) failed: %m", path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str = t_str_new(128);
b76bc28de1c65b7ed6b19c7873b294d3742f5d57Timo Sirainen /* if we have no limits, write 0S instead of an empty line */
b76bc28de1c65b7ed6b19c7873b294d3742f5d57Timo Sirainen if (_root->bytes_limit != 0 || _root->count_limit == 0) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen str_printfa(str, "%lluS",
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen (unsigned long long)_root->bytes_limit);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen if (_root->count_limit != 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (str_len(str) > 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_append_c(str, ',');
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen str_printfa(str, "%lluC",
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen (unsigned long long)_root->count_limit);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_printfa(str, "\n%llu %llu\n",
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen (unsigned long long)root->total_bytes,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen (unsigned long long)root->total_count);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (write_full(fd, str_data(str), str_len(str)) < 0) {
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen i_error("write_full(%s) failed: %m", str_c(temp_path));
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen i_close_fd(&fd);
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen if (unlink(str_c(temp_path)) < 0)
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen i_error("unlink(%s) failed: %m", str_c(temp_path));
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen i_close_fd(&fd);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen if (rename(str_c(temp_path), path) < 0) {
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen i_error("rename(%s, %s) failed: %m", str_c(temp_path), path);
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen if (unlink(str_c(temp_path)) < 0 && errno != ENOENT)
52ec61a8d49986fa140639d371f75ac078c47be5Timo Sirainen i_error("unlink(%s) failed: %m", str_c(temp_path));
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void maildirsize_recalculate_init(struct maildir_quota_root *root)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen root->total_bytes = root->total_count = 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen root->recalc_last_stamp = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic int maildirsize_recalculate_namespace(struct maildir_quota_root *root,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *ns)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_list_context *ctx;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *dir;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen time_t mtime;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx = maildir_list_init(root, ns->list);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen while ((dir = maildir_list_next(ctx, &mtime)) != NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (mtime > root->recalc_last_stamp)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen root->recalc_last_stamp = mtime;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (maildir_sum_dir(dir, &root->total_bytes,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen &root->total_count) < 0)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = -1;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (maildir_list_deinit(ctx) < 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic void maildirsize_rebuild_later(struct maildir_quota_root *root)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (!root->root.set->force_default_rule) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* FIXME: can't unlink(), because the limits would be lost. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (unlink(root->maildirsize_path) < 0 &&
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen errno != ENOENT && errno != ESTALE)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_error("unlink(%s) failed: %m", root->maildirsize_path);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int maildirsize_recalculate_finish(struct maildir_quota_root *root,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen int ret)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (ret == 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* maildir didn't change, we can write the maildirsize file */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = maildirsize_write(root, root->maildirsize_path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (ret != 0)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen maildirsize_rebuild_later(root);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int maildirsize_recalculate(struct maildir_quota_root *root)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *const *namespaces;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen int ret = 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen maildirsize_recalculate_init(root);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* count mails from all namespaces */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen namespaces = array_get(&root->root.quota->namespaces, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (!quota_root_is_namespace_visible(&root->root, namespaces[i]))
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen continue;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (maildirsize_recalculate_namespace(root, namespaces[i]) < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (ret == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* check if any of the directories have changed */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (!quota_root_is_namespace_visible(&root->root,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen namespaces[i]))
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen continue;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ret = maildirs_check_have_changed(root, namespaces[i],
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen root->recalc_last_stamp);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (ret != 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return maildirsize_recalculate_finish(root, ret);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainenstatic bool
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainenmaildir_parse_limit(const char *str, uint64_t *bytes_r, uint64_t *count_r)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen const char *const *limit;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen unsigned long long value;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen char *pos;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen bool ret = TRUE;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen *bytes_r = 0;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen *count_r = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen /* 0 values mean unlimited */
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen for (limit = t_strsplit(str, ","); *limit != NULL; limit++) {
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen value = strtoull(*limit, &pos, 10);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (pos[0] != '\0' && pos[1] == '\0') {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen switch (pos[0]) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen case 'C':
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen if (value != 0)
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen *count_r = value;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen case 'S':
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen if (value != 0)
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen *bytes_r = value;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen break;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen default:
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen ret = FALSE;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen } else {
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen ret = FALSE;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen return ret;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen}
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainenstatic int maildirsize_parse(struct maildir_quota_root *root,
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen int fd, const char *const *lines)
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen{
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen struct quota_root *_root = &root->root;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen uint64_t message_bytes_limit, message_count_limit;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen long long bytes_diff, total_bytes;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen int count_diff, total_count;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen unsigned int line_count = 0;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen if (*lines == NULL)
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen return -1;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen /* first line contains the limits */
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen (void)maildir_parse_limit(lines[0], &message_bytes_limit,
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen &message_count_limit);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen /* truncate too high limits to signed 64bit int range */
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen if (message_bytes_limit >= (1ULL << 63))
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen message_bytes_limit = (1ULL << 63) - 1;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen if (message_count_limit >= (1ULL << 63))
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen message_count_limit = (1ULL << 63) - 1;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen if (root->root.bytes_limit == (int64_t)message_bytes_limit &&
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen root->root.count_limit == (int64_t)message_count_limit) {
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen /* limits haven't changed */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen } else if (root->root.set->force_default_rule) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen /* we know the limits and they've changed.
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen the file must be rewritten. */
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen return 0;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen } else {
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen /* we're using limits from the file. */
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen root->root.bytes_limit = message_bytes_limit;
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen root->root.count_limit = message_count_limit;
d761c26f2bbf514e0fc0c6ed9bc52627a4179eabTimo Sirainen quota_root_recalculate_relative_rules(root->root.set,
d761c26f2bbf514e0fc0c6ed9bc52627a4179eabTimo Sirainen message_bytes_limit,
d761c26f2bbf514e0fc0c6ed9bc52627a4179eabTimo Sirainen message_count_limit);
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen }
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen
ba3f68dfb8475299f43125c3e86985a713013c5dTimo Sirainen if (*lines == NULL) {
ba3f68dfb8475299f43125c3e86985a713013c5dTimo Sirainen /* no quota lines. rebuild it. */
ba3f68dfb8475299f43125c3e86985a713013c5dTimo Sirainen return 0;
ba3f68dfb8475299f43125c3e86985a713013c5dTimo Sirainen }
ba3f68dfb8475299f43125c3e86985a713013c5dTimo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* rest of the lines contains <bytes> <count> diffs */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen total_bytes = 0; total_count = 0;
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen for (lines++; *lines != NULL; lines++, line_count++) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (sscanf(*lines, "%lld %d", &bytes_diff, &count_diff) != 2)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen total_bytes += bytes_diff;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen total_count += count_diff;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (total_bytes < 0 || total_count < 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* corrupted */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen if ((total_bytes > _root->bytes_limit && _root->bytes_limit != 0) ||
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen (total_count > _root->count_limit && _root->count_limit != 0)) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* we're over quota. don't trust these values if the file
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen contains more than the initial summary line, or if the file
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen is older than 15 minutes. */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct stat st;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (line_count > 1)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (fstat(fd, &st) < 0 ||
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen st.st_mtime < ioloop_time - MAILDIRSIZE_STALE_SECS)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->total_bytes = (uint64_t)total_bytes;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->total_count = (uint64_t)total_count;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return 1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainenstatic int maildirsize_open(struct maildir_quota_root *root)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (root->fd != -1) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (close(root->fd) < 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("close(%s) failed: %m", root->maildirsize_path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen root->fd = nfs_safe_open(root->maildirsize_path, O_RDWR | O_APPEND);
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen if (root->fd == -1) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (errno == ENOENT)
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen return 0;
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen i_error("open(%s) failed: %m", root->maildirsize_path);
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen return 1;
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen}
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainenstatic bool maildirsize_has_changed(struct maildir_quota_root *root)
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen{
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen struct stat st1, st2;
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen if (dotlock_settings.nfs_flush) {
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen nfs_flush_file_handle_cache(root->maildirsize_path);
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen nfs_flush_attr_cache_unlocked(root->maildirsize_path);
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen }
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen if (root->fd == -1)
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen return TRUE;
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen if (stat(root->maildirsize_path, &st1) < 0)
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen return TRUE;
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen if (fstat(root->fd, &st2) < 0)
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen return TRUE;
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen
ed22384e18b005c6a518478c515e7bfdb9e110eeTimo Sirainen return root->last_size != st2.st_size || st1.st_ino != st2.st_ino ||
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen !CMP_DEV_T(st1.st_dev, st2.st_dev);
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen}
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainenstatic int maildirsize_read(struct maildir_quota_root *root, bool *retry)
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen{
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen char buf[5120+1];
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen unsigned int i, size;
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen bool retry_estale = *retry;
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen int ret;
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen *retry = FALSE;
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen if (!maildirsize_has_changed(root))
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen return 1;
7be291ff7fcb7d10a5f96db00282a40287b471c5Timo Sirainen
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen if ((ret = maildirsize_open(root)) <= 0)
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
4c5272b168b4d71a95a08d48cc5b29cd7b27f193Timo Sirainen /* @UNSAFE */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen size = 0;
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen while ((ret = read(root->fd, buf + size, sizeof(buf)-1 - size)) != 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (ret < 0) {
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen if (errno == ESTALE && retry_estale) {
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen *retry = TRUE;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("read(%s) failed: %m", root->maildirsize_path);
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen break;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen size += ret;
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen if (size >= sizeof(buf)-1) {
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen /* we'll need to recalculate the quota */
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen break;
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen /* try to use the file even if we ran into some error. if we don't have
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen forced limits, we'll need to read the header to get them */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->total_bytes = root->total_count = 0;
ed22384e18b005c6a518478c515e7bfdb9e110eeTimo Sirainen root->last_size = size;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen /* skip the last line if there's no LF at the end. Remove the last LF
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen so we don't get one empty line in the strsplit. */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen while (size > 0 && buf[size-1] != '\n') size--;
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen if (size > 0) size--;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen buf[size] = '\0';
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen if (ret < 0 && size == 0) {
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen /* the read failed and there's no usable header, fail. */
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&root->fd);
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen return -1;
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen }
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen /* If there are any NUL bytes, the file is broken. */
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen for (i = 0; i < size; i++) {
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen if (buf[i] == '\0')
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen break;
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen }
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen if (i == size &&
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen maildirsize_parse(root, root->fd, t_strsplit(buf, "\n")) > 0 &&
37f5e4f22d8f0d6aeca6536fba84b396c2196364Timo Sirainen ret == 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = 1;
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen else {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* broken file / need recalculation */
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&root->fd);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainenstatic bool maildirquota_limits_init(struct maildir_quota_root *root)
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen{
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen struct mailbox_list *list;
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen struct mail_storage *storage;
7ca2a9f1cca63cbc2ebffc185c7e5a2b32bc2780Timo Sirainen
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen if (root->limits_initialized)
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen return root->maildirsize_path != NULL;
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen root->limits_initialized = TRUE;
7ca2a9f1cca63cbc2ebffc185c7e5a2b32bc2780Timo Sirainen
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen if (root->maildirsize_ns == NULL) {
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen i_assert(root->maildirsize_path == NULL);
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen return FALSE;
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen }
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen i_assert(root->maildirsize_path != NULL);
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen list = root->maildirsize_ns->list;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (mailbox_list_get_storage(&list, "", &storage) == 0 &&
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen strcmp(storage->name, MAILDIR_STORAGE_NAME) != 0) {
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen /* non-maildir namespace, skip */
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen if ((storage->class_flags &
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen MAIL_STORAGE_CLASS_FLAG_NOQUOTA) == 0) {
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen i_warning("quota: Namespace '%s' is not Maildir, "
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen "skipping for Maildir++ quota",
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen root->maildirsize_ns->prefix);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen }
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen root->maildirsize_path = NULL;
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen return FALSE;
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen }
282142061c85f892e52154e81b9f8d4393b3b0c3Timo Sirainen return TRUE;
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen}
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainenstatic int maildirquota_read_limits(struct maildir_quota_root *root)
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen{
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen bool retry = TRUE;
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen int ret, n = 0;
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen if (!maildirquota_limits_init(root))
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen return 1;
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen do {
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen if (n == NFS_ESTALE_RETRY_COUNT)
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen retry = FALSE;
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen T_BEGIN {
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen ret = maildirsize_read(root, &retry);
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen } T_END;
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen n++;
566a436ce4ad942d2f13673a2dbec18aa3baee1eTimo Sirainen } while (ret == -1 && retry);
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen return ret;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen}
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainenstatic int
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainenmaildirquota_refresh(struct maildir_quota_root *root, bool *recalculated_r)
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen{
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen int ret;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen *recalculated_r = FALSE;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen ret = maildirquota_read_limits(root);
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen if (ret == 0) {
9f018a85c93145e789ea5365718cfc6dd954a913Timo Sirainen if (root->root.bytes_limit == 0 &&
9f018a85c93145e789ea5365718cfc6dd954a913Timo Sirainen root->root.count_limit == 0 &&
9f018a85c93145e789ea5365718cfc6dd954a913Timo Sirainen root->root.set->default_rule.bytes_limit == 0 &&
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen root->root.set->default_rule.count_limit == 0) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen /* no quota */
53101f0e1e998d9bf718689fdb1363c5f0a07b0fTimo Sirainen if (!root->root.set->force_default_rule)
53101f0e1e998d9bf718689fdb1363c5f0a07b0fTimo Sirainen return 0;
53101f0e1e998d9bf718689fdb1363c5f0a07b0fTimo Sirainen /* explicitly specified 0 as quota. keep the quota
53101f0e1e998d9bf718689fdb1363c5f0a07b0fTimo Sirainen updated even if it's not enforced. */
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen }
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = maildirsize_recalculate(root);
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen if (ret == 0)
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen *recalculated_r = TRUE;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret < 0 ? -1 : 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic int maildirsize_update(struct maildir_quota_root *root,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int count_diff, int64_t bytes_diff)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen char str[MAX_INT_STRLEN*2 + 2 + 1];
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
0461b5dfd1ac6b5ae49a2f1ab6ffdc2e5e2ee7c2Timo Sirainen if (count_diff == 0 && bytes_diff == 0)
0461b5dfd1ac6b5ae49a2f1ab6ffdc2e5e2ee7c2Timo Sirainen return 0;
0461b5dfd1ac6b5ae49a2f1ab6ffdc2e5e2ee7c2Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* We rely on O_APPEND working in here. That isn't NFS-safe, but it
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen isn't necessarily that bad because the file is recreated once in
a8fcd55e88550ebb905249825bdb1eec7b9667ffTimo Sirainen a while, and sooner if corruption causes calculations to go
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen over quota. This is also how Maildir++ spec specifies it should be
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen done.. */
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (i_snprintf(str, sizeof(str), "%lld %d\n",
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen (long long)bytes_diff, count_diff) < 0)
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen i_unreached();
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (write_full(root->fd, str, strlen(str)) < 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (errno == ESTALE) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* deleted/replaced already, ignore */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("write_full(%s) failed: %m",
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen root->maildirsize_path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen } else {
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen /* close the file to force a flush with NFS */
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen if (close(root->fd) < 0) {
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen ret = -1;
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen if (errno != ESTALE)
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen i_error("close(%s) failed: %m", root->maildirsize_path);
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen }
15093714cd8484c0b7781e00de5ab6f991c558dcTimo Sirainen root->fd = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic struct quota_root *maildir_quota_alloc(void)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_quota_root *root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root = i_new(struct maildir_quota_root, 1);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->fd = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return &root->root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainenstatic int maildir_quota_init(struct quota_root *_root, const char *args,
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen const char **error_r)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen const char *const *tmp;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (args == NULL)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return 0;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen for (tmp = t_strsplit(args, ":"); *tmp != NULL; tmp++) {
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (strcmp(*tmp, "noenforcing") == 0)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen _root->no_enforcing = TRUE;
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen else if (strcmp(*tmp, "ignoreunlimited") == 0)
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen _root->disable_unlimited_tracking = TRUE;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen else if (strncmp(*tmp, "ns=", 3) == 0)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen _root->ns_prefix = p_strdup(_root->pool, *tmp + 3);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen else {
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen *error_r = t_strdup_printf("Invalid parameter: %s", *tmp);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return -1;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return 0;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic void maildir_quota_deinit(struct quota_root *_root)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
6ce34c7a2462412af024a0320911a3fe1c04b264Timo Sirainen if (root->fd != -1)
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&root->fd);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen i_free(root);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainenstatic bool
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenmaildir_quota_parse_rule(struct quota_root_settings *root_set ATTR_UNUSED,
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen struct quota_rule *rule,
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen const char *str, const char **error_r)
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen{
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen uint64_t bytes, count;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
5a8f244ec97d99fef45e86a0aa24b8f63b219616Timo Sirainen if (strcmp(str, "NOQUOTA") == 0) {
5a8f244ec97d99fef45e86a0aa24b8f63b219616Timo Sirainen bytes = 0;
5a8f244ec97d99fef45e86a0aa24b8f63b219616Timo Sirainen count = 0;
5a8f244ec97d99fef45e86a0aa24b8f63b219616Timo Sirainen } else if (!maildir_parse_limit(str, &bytes, &count)) {
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen *error_r = "Invalid Maildir++ quota rule";
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen return FALSE;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen }
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen rule->bytes_limit = bytes;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen rule->count_limit = count;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen return TRUE;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen}
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainenstatic int maildir_quota_init_limits(struct quota_root *_root)
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen{
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen return maildirquota_read_limits(root) < 0 ? -1 : 0;
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen}
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenmaildir_quota_root_namespace_added(struct quota_root *_root,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *ns)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *control_dir;
677e22747b82bf15b339e31d1d0106d62bf806daTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (root->maildirsize_path != NULL)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (!mailbox_list_get_root_path(ns->list, MAILBOX_LIST_PATH_TYPE_CONTROL,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen &control_dir))
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen i_unreached();
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen root->maildirsize_ns = ns;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen root->maildirsize_path =
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen p_strconcat(_root->pool, control_dir,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "/"MAILDIRSIZE_FILENAME, NULL);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic void
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenmaildir_quota_namespace_added(struct quota *quota, struct mail_namespace *ns)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root **roots;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get_modifiable(&quota->roots, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (roots[i]->backend.name == quota_backend_maildir.name &&
e6dfe937343e7d5e2a9f5a188a44d922460fbbefTimo Sirainen ((roots[i]->ns_prefix == NULL &&
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) ||
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen roots[i]->ns == ns))
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen maildir_quota_root_namespace_added(roots[i], ns);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic const char *const *
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainenmaildir_quota_root_get_resources(struct quota_root *root ATTR_UNUSED)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen static const char *resources_both[] = {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen QUOTA_NAME_STORAGE_KILOBYTES,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen QUOTA_NAME_MESSAGES,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen NULL
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen };
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return resources_both;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic int
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenmaildir_quota_get_resource(struct quota_root *_root, const char *name,
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen uint64_t *value_r)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen bool recalculated;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen if (maildirquota_refresh(root, &recalculated) < 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
5a001dd0848310e9ed6622442329ff346b189257Timo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen *value_r = root->total_bytes;
5a001dd0848310e9ed6622442329ff346b189257Timo Sirainen } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen *value_r = root->total_count;
5a001dd0848310e9ed6622442329ff346b189257Timo Sirainen } else
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen return 0;
8b10d65deb01f351172408ab9540413d60d5fe3bTimo Sirainen return 1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic int
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenmaildir_quota_update(struct quota_root *_root,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_transaction_context *ctx)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen bool recalculated;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen if (!maildirquota_limits_init(root)) {
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen /* no limits */
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen return 0;
e394583a7c1121599799a35f205bf237f6a1ee04Timo Sirainen }
63af428111bdf1975b06da83f98d072210514a36Timo Sirainen
63af428111bdf1975b06da83f98d072210514a36Timo Sirainen /* even though we don't really care about the limits in here ourself,
63af428111bdf1975b06da83f98d072210514a36Timo Sirainen we do want to make sure the header gets updated if the limits have
63af428111bdf1975b06da83f98d072210514a36Timo Sirainen changed. also this makes sure the maildirsize file is created if
63af428111bdf1975b06da83f98d072210514a36Timo Sirainen it doesn't exist. */
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen if (maildirquota_refresh(root, &recalculated) < 0)
63af428111bdf1975b06da83f98d072210514a36Timo Sirainen return -1;
0223007c8a72339fe40a732710203afb82f108a6Timo Sirainen
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen if (recalculated) {
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen /* quota was just recalculated and it already contains the changes
12aae0081cbdf86bfb330ab087a3824466234255Timo Sirainen we wanted to do. */
b694893eb825c0e1cb4bec008fbf642311894c1aTimo Sirainen } else if (root->fd == -1)
b694893eb825c0e1cb4bec008fbf642311894c1aTimo Sirainen (void)maildirsize_recalculate(root);
b694893eb825c0e1cb4bec008fbf642311894c1aTimo Sirainen else if (ctx->recalculate) {
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&root->fd);
b694893eb825c0e1cb4bec008fbf642311894c1aTimo Sirainen (void)maildirsize_recalculate(root);
46c6ebbfafc684c8e97287c9755f9ce5bc002952Timo Sirainen } else if (maildirsize_update(root, ctx->count_used, ctx->bytes_used) < 0) {
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen if (root->fd != -1)
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&root->fd);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen maildirsize_rebuild_later(root);
46c6ebbfafc684c8e97287c9755f9ce5bc002952Timo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstruct quota_backend quota_backend_maildir = {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen "maildir",
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen maildir_quota_alloc,
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen maildir_quota_init,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen maildir_quota_deinit,
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen maildir_quota_parse_rule,
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen maildir_quota_init_limits,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen maildir_quota_namespace_added,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen maildir_quota_root_get_resources,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen maildir_quota_get_resource,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen maildir_quota_update,
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen NULL,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen NULL
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen};