quota-maildir.c revision 0563ad5c7f179554623682f6fd7b98596901b49f
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "nfs-workarounds.h"
#include "file-dotlock.h"
#include "read-full.h"
#include "write-full.h"
#include "str.h"
#include "maildir-storage.h"
#include "quota-private.h"
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#define MAILDIRSIZE_FILENAME "maildirsize"
struct maildir_quota_root {
struct quota_root root;
const char *maildirsize_path;
int fd;
unsigned int limits_initialized:1;
unsigned int master_message_limits:1;
};
struct maildir_list_context {
struct mail_storage *storage;
struct mailbox_list_iterate_context *iter;
struct mailbox_info *info;
int state;
};
extern struct quota_backend quota_backend_maildir;
struct dotlock_settings dotlock_settings = {
};
{
const char *p;
int ret = 0;
return 0;
return -1;
}
continue;
if (p != NULL) {
/* ,S=nnnn[:,] */
p += 3;
if (*p != ':' && *p != '\0' && *p != ',') {
/* not in expected format, fallback to stat() */
} else {
*total_bytes += num;
*total_count += 1;
}
}
*total_count += 1;
ret = -1;
}
}
}
return -1;
}
return ret;
}
static struct maildir_list_context *
{
struct maildir_list_context *ctx;
return ctx;
}
static const char *
{
const char *path;
bool is_file;
for (;;) {
return NULL;
}
t_push();
&is_file);
t_pop();
break;
/* ignore if the directory got lost, stale or if it was
actually a file and not a directory */
}
}
}
{
return ret;
}
static int
{
struct maildir_list_context *ctx;
int ret = 0;
if (mtime > latest_mtime) {
ret = 1;
break;
}
}
if (maildir_list_deinit(ctx) < 0)
return -1;
return ret;
}
{
int fd;
if (fd == -1) {
/* someone's just in the middle of updating it */
return -1;
}
return -1;
}
(unsigned long long)root->message_bytes_limit);
}
(unsigned long long)root->message_count_limit);
}
(unsigned long long)root->total_bytes,
(unsigned long long)root->total_count);
return -1;
}
/* keep the fd open since we might want to update it later */
if (file_dotlock_replace(&dotlock,
return -1;
}
return 0;
}
{
root->recalc_last_stamp = 0;
}
struct mail_storage *storage)
{
struct maildir_list_context *ctx;
const char *dir;
int ret = 0;
t_push();
&root->total_count) < 0)
ret = -1;
t_pop();
}
if (maildir_list_deinit(ctx) < 0)
ret = -1;
return ret;
}
int ret)
{
if (ret == 0) {
/* maildir didn't change, we can write the maildirsize file */
}
if (ret != 0) {
/* make sure it gets rebuilt later */
i_error("unlink(%s) failed: %m",
}
}
return ret;
}
{
struct mail_storage *const *storages;
unsigned int i, count;
int ret = 0;
/* count mails from all storages */
for (i = 0; i < count; i++) {
ret = -1;
break;
}
}
if (ret == 0) {
/* check if any of the directories have changed */
for (i = 0; i < count; i++) {
if (ret != 0)
break;
}
}
}
{
unsigned long long bytes;
long long bytes_diff, total_bytes;
int count_diff, total_count;
unsigned int line_count = 0;
const char *const *limit;
char *pos;
return -1;
/* first line contains the limits. 0 value mean unlimited. */
switch (pos[0]) {
case 'C':
if (bytes != 0)
break;
case 'S':
if (bytes != 0)
break;
}
}
}
if (!root->master_message_limits) {
/* we don't know the limits, use whatever the file says */
/* we know the limits and they've changed.
the file must be rewritten. */
return 0;
}
/* rest of the lines contains <bytes> <count> diffs */
total_bytes = 0; total_count = 0;
return -1;
}
/* we end always with LF, which shows up as empty last line. there
should be no other empty lines */
return -1;
if (total_bytes < 0 || total_count < 0) {
/* corrupted */
return -1;
}
/* we're over quota. don't trust these values if the file
contains more than the initial summary line, or if the file
is older than 15 minutes. */
if (line_count > 1)
return 0;
return 0;
}
return 1;
}
{
unsigned int i, size;
t_push();
}
if (fd == -1) {
ret = 0;
else {
ret = -1;
}
t_pop();
return ret;
}
/* @UNSAFE */
size = 0;
if (ret < 0) {
break;
}
}
/* error / recalculation needed. */
t_pop();
return ret < 0 ? -1 : 0;
}
/* file is smaller than 5120 bytes, which means we can use it */
/* skip the last line if there's no LF at the end. Remove the last LF
so we don't get one empty line in the strsplit. */
/* If there are any NUL bytes, the file is broken. */
for (i = 0; i < size; i++) {
if (buf[i] == '\0')
break;
}
if (i == size &&
ret = 1;
} else {
/* broken file / need recalculation */
ret = 0;
}
t_pop();
return ret;
}
{
}
}
{
int ret;
if (!root->limits_initialized)
if (ret == 0) {
/* no quota */
return 0;
}
}
return ret < 0 ? -1 : 0;
}
{
const char *str;
int ret = 0;
if (count_diff == 0 && bytes_diff == 0)
return 0;
t_push();
/* We rely on O_APPEND working in here. That isn't NFS-safe, but it
isn't necessarily that bad because the file is recreated once in
a while, and sooner if corruption cases calculations to go
over quota. This is also how Maildir++ spec specifies it should be
done.. */
ret = -1;
} else {
i_error("write_full(%s) failed: %m",
}
}
t_pop();
return ret;
}
static struct quota_root *maildir_quota_alloc(void)
{
struct maildir_quota_root *root;
}
{
}
static void
struct mail_storage *storage)
{
const char *control_dir;
return;
}
static void
struct mail_storage *_storage)
{
struct maildir_storage *storage =
(struct maildir_storage *)_storage;
struct quota_root **roots;
unsigned int i, count;
return;
for (i = 0; i < count; i++) {
}
/* For newly generated filenames add ,S=size. */
}
static const char *const *
{
static const char *resources_both[] = {
};
return resources_both;
}
static int
{
if (maildirquota_refresh(root) < 0)
return -1;
else
return 0;
return 1;
}
static int
struct quota_transaction_context *ctx)
{
struct maildir_quota_root *root =
(struct maildir_quota_root *) _root;
/* if writing fails, we don't care all that much */
ctx->bytes_used);
}
return 0;
}
struct quota_backend quota_backend_maildir = {
"maildir",
{
NULL,
}
};