quota-maildir.c revision bd7b1a9000b12349e2a99bb43b3ce8b96a18e92b
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen/* Copyright (C) 2006 Timo Sirainen */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "lib.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "array.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "ioloop.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "nfs-workarounds.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "file-dotlock.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "read-full.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "write-full.h"
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen#include "str.h"
677e22747b82bf15b339e31d1d0106d62bf806daTimo Sirainen#include "maildir-storage.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
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *maildirsize_path;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen uint64_t message_bytes_limit;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen uint64_t message_count_limit;
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;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen unsigned int limits_initialized:1;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen unsigned int master_message_limits:1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen};
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstruct maildir_list_context {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct mail_storage *storage;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct mailbox_list_iterate_context *iter;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen 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
8887bf3757d51d73887dd20b1db3334d867d3817Timo Sirainenstruct dotlock_settings dotlock_settings = {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen MEMBER(temp_prefix) NULL,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen MEMBER(lock_suffix) NULL,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen MEMBER(timeout) 0,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen MEMBER(stale_timeout) 30,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen MEMBER(callback) NULL,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen MEMBER(context) NULL,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen MEMBER(use_excl_lock) FALSE
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 *
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenmaildir_list_init(struct mail_storage *storage)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_list_context *ctx;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx = i_new(struct maildir_list_context, 1);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ctx->storage = storage;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx->path = str_new(default_pool, 512);
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen ctx->iter = mailbox_list_iter_init(mail_storage_get_list(storage), "*",
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen MAILBOX_LIST_ITER_FAST_FLAGS);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ctx;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic const char *
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenmaildir_list_next(struct maildir_list_context *ctx, time_t *mtime_r)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct stat st;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen const char *path;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen bool is_file;
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;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_push();
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen path = mail_storage_get_mailbox_path(ctx->storage,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ctx->info->name,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen &is_file);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_truncate(ctx->path, 0);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_append(ctx->path, path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_append(ctx->path, ctx->state == 0 ? "/new" : "/cur");
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_pop();
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
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen *mtime_r = st.st_size;
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
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenmaildirs_check_have_changed(struct mail_storage *storage, 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
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx = maildir_list_init(storage);
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{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct dotlock *dotlock;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen string_t *str;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int fd;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen i_assert(root->fd == -1);
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen
8887bf3757d51d73887dd20b1db3334d867d3817Timo Sirainen dotlock_settings.use_excl_lock = getenv("DOTLOCK_USE_EXCL") != NULL;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen fd = file_dotlock_open(&dotlock_settings, path,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen DOTLOCK_CREATE_FLAG_NONBLOCK, &dotlock);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (fd == -1) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (errno == EAGAIN) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* someone's just in the middle of updating it */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("file_dotlock_open(%s) failed: %m", path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str = t_str_new(128);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (root->message_bytes_limit != (uint64_t)-1) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen str_printfa(str, "%lluS",
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen (unsigned long long)root->message_bytes_limit);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (root->message_count_limit != (uint64_t)-1) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (str_len(str) > 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen str_append_c(str, ',');
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen str_printfa(str, "%lluC",
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen (unsigned long long)root->message_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) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("write_full(%s) failed: %m", path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen file_dotlock_delete(&dotlock);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen /* keep the fd open since we might want to update it later */
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen if (file_dotlock_replace(&dotlock,
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("file_dotlock_replace(%s) failed: %m", path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
b83f52b3ac3736d7f509db7ba80360d89f8a776dTimo Sirainen root->fd = fd;
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
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int maildirsize_recalculate_storage(struct maildir_quota_root *root,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct mail_storage *storage)
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
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ctx = maildir_list_init(storage);
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
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_push();
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (maildir_sum_dir(dir, &root->total_bytes,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen &root->total_count) < 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_pop();
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{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (!root->master_message_limits) {
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{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct mail_storage *const *storages;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen int ret = 0;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen maildirsize_recalculate_init(root);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* count mails from all storages */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen storages = array_get(&root->root.quota->storages, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (maildirsize_recalculate_storage(root, storages[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++) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = maildirs_check_have_changed(storages[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
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic int maildirsize_parse(struct maildir_quota_root *root,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int fd, const char *const *lines)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen unsigned long long bytes;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen uint64_t message_bytes_limit, message_count_limit;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen long long bytes_diff, total_bytes;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int count_diff, total_count;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen unsigned int line_count = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen const char *const *limit;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen char *pos;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (*lines == NULL)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
915a93236b3db747fb5a56db30beb95a41ee0221Timo Sirainen /* first line contains the limits. 0 value mean unlimited. */
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen message_bytes_limit = (uint64_t)-1;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen message_count_limit = (uint64_t)-1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen for (limit = t_strsplit(lines[0], ","); *limit != NULL; limit++) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen bytes = strtoull(*limit, &pos, 10);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (pos[0] != '\0' && pos[1] == '\0') {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen switch (pos[0]) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen case 'C':
915a93236b3db747fb5a56db30beb95a41ee0221Timo Sirainen if (bytes != 0)
915a93236b3db747fb5a56db30beb95a41ee0221Timo Sirainen message_count_limit = bytes;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen case 'S':
915a93236b3db747fb5a56db30beb95a41ee0221Timo Sirainen if (bytes != 0)
915a93236b3db747fb5a56db30beb95a41ee0221Timo Sirainen message_bytes_limit = bytes;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen if (!root->master_message_limits) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen /* we don't know the limits, use whatever the file says */
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen root->message_bytes_limit = message_bytes_limit;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen root->message_count_limit = message_count_limit;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen } else if (root->message_bytes_limit != message_bytes_limit ||
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen root->message_count_limit != message_count_limit) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen /* we know the limits and they've changed.
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen the file must be rewritten. */
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen return 0;
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 /* we end always with LF, which shows up as empty last line. there
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen should be no other empty lines */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (lines[1] != NULL)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (total_bytes < 0 || total_count < 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* corrupted */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if ((uint64_t)total_bytes > root->message_bytes_limit ||
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen (uint64_t)total_count > root->message_count_limit) {
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
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int maildirsize_read(struct maildir_quota_root *root)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen char buf[5120+1];
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen unsigned int i, size;
5f24517ae602ca828bc7e197913f51d55d79c9bcTimo Sirainen int fd, ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_push();
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 root->fd = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen fd = nfs_safe_open(root->maildirsize_path, O_RDWR | O_APPEND);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (fd == -1) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (errno == ENOENT)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen else {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = -1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("open(%s) failed: %m", root->maildirsize_path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_pop();
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
4c5272b168b4d71a95a08d48cc5b29cd7b27f193Timo Sirainen /* @UNSAFE */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen size = 0;
4c5272b168b4d71a95a08d48cc5b29cd7b27f193Timo Sirainen while (size < sizeof(buf)-1 &&
4c5272b168b4d71a95a08d48cc5b29cd7b27f193Timo Sirainen (ret = read(fd, buf + size, sizeof(buf)-1 - size)) != 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (ret < 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen if (errno == ESTALE)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen break;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("read(%s) failed: %m", root->maildirsize_path);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen size += ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
4c5272b168b4d71a95a08d48cc5b29cd7b27f193Timo Sirainen if (ret < 0 || size >= sizeof(buf)-1) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* error / recalculation needed. */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen (void)close(fd);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_pop();
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret < 0 ? -1 : 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* file is smaller than 5120 bytes, which means we can use it */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->total_bytes = root->total_count = 0;
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
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 &&
0563ad5c7f179554623682f6fd7b98596901b49fTimo Sirainen maildirsize_parse(root, fd, t_strsplit(buf, "\n")) > 0) {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->fd = fd;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = 1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen } else {
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen /* broken file / need recalculation */
aa9c91ee2fcf0ee65d82d0cdc4a58ab1a7002c26Timo Sirainen (void)close(fd);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->fd = -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_pop();
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainenstatic void maildirquota_init_limits(struct maildir_quota_root *root)
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen{
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen root->limits_initialized = TRUE;
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen if (root->root.default_rule.bytes_limit != 0 ||
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen root->root.default_rule.count_limit != 0) {
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen root->master_message_limits = TRUE;
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen root->message_bytes_limit = root->root.default_rule.bytes_limit;
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen root->message_count_limit = root->root.default_rule.count_limit;
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen }
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen}
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int maildirquota_refresh(struct maildir_quota_root *root)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int ret;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen if (!root->limits_initialized)
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen maildirquota_init_limits(root);
c73bb576f140e1c1aae57fd607d02b418afe2c3fTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = maildirsize_read(root);
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen if (ret == 0) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen if (root->message_bytes_limit == (uint64_t)-1 &&
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen root->message_count_limit == (uint64_t)-1) {
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen /* no quota */
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen return 0;
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen }
2d71e0ea3006576961b47d91d564d31771676624Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = maildirsize_recalculate(root);
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{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen const char *str;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen int ret = 0;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
0461b5dfd1ac6b5ae49a2f1ab6ffdc2e5e2ee7c2Timo Sirainen if (count_diff == 0 && bytes_diff == 0)
0461b5dfd1ac6b5ae49a2f1ab6ffdc2e5e2ee7c2Timo Sirainen return 0;
0461b5dfd1ac6b5ae49a2f1ab6ffdc2e5e2ee7c2Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_push();
cb951d3282610a9a0960230865bc5f3e3347b203Timo 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.. */
0461b5dfd1ac6b5ae49a2f1ab6ffdc2e5e2ee7c2Timo Sirainen str = t_strdup_printf("%lld %d\n", (long long)bytes_diff, count_diff);
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 }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen t_pop();
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 root->message_bytes_limit = (uint64_t)-1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen root->message_count_limit = (uint64_t)-1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return &root->root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo 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)
6ce34c7a2462412af024a0320911a3fe1c04b264Timo Sirainen (void)close(root->fd);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen i_free(root);
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenmaildir_quota_root_storage_added(struct quota_root *_root,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct mail_storage *storage)
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
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen control_dir = mail_storage_get_mailbox_control_dir(storage, "");
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
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenmaildir_quota_storage_added(struct quota *quota,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct mail_storage *_storage)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct maildir_storage *storage =
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (struct maildir_storage *)_storage;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct quota_root **roots;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (strcmp(_storage->name, "maildir") != 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen roots = array_get_modifiable(&quota->roots, &count);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (roots[i]->backend.name == quota_backend_maildir.name)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen maildir_quota_root_storage_added(roots[i], _storage);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* For newly generated filenames add ,S=size. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen storage->save_size_in_filename = TRUE;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen}
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainenstatic const char *const *
cb951d3282610a9a0960230865bc5f3e3347b203Timo 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,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen uint64_t *value_r, uint64_t *limit __attr_unused__)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (maildirquota_refresh(root) < 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen return -1;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen *value_r = root->total_bytes;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen *value_r = root->total_count;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo 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{
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen struct maildir_quota_root *root =
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (struct maildir_quota_root *) _root;
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (root->fd == -1 || ctx->recalculate ||
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen maildirsize_update(root, ctx->count_used, ctx->bytes_used) < 0)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen maildirsize_rebuild_later(root);
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,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen NULL,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen maildir_quota_deinit,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen maildir_quota_storage_added,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen maildir_quota_root_get_resources,
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen maildir_quota_get_resource,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen maildir_quota_update
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen }
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen};