quota-maildir.c revision 7b3b617e946d5b32078baa821f5fc05f775e1dfe
/* Copyright (c) 2006-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "nfs-workarounds.h"
#include "safe-mkstemp.h"
#include "mkdir-parents.h"
#include "read-full.h"
#include "write-full.h"
#include "str.h"
#include "maildir-storage.h"
#include "mailbox-list-private.h"
#include "quota-private.h"
#include <stdio.h>
#include <dirent.h>
#define MAILDIRSIZE_FILENAME "maildirsize"
struct maildir_quota_root {
struct quota_root root;
struct mail_namespace *maildirsize_ns;
const char *maildirsize_path;
int fd;
bool limits_initialized:1;
};
struct maildir_list_context {
struct mailbox_list *list;
struct maildir_quota_root *root;
struct mailbox_list_iterate_context *iter;
const struct mailbox_info *info;
int state;
};
extern struct quota_backend quota_backend_maildir;
static struct dotlock_settings dotlock_settings = {
.timeout = 0,
.stale_timeout = 30
};
{
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;
}
{
const char *path, *storage_name;
&path) > 0) {
"/new" : "/cur");
}
}
static const char *
{
struct quota_rule *rule;
for (;;) {
return NULL;
/* mailbox not included in quota */
continue;
}
}
if (!maildir_set_next_path(ctx)) {
continue;
}
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;
}
{
const struct mail_storage_settings *set =
struct mail_namespace *const *namespaces;
unsigned int i, count;
struct mailbox_permissions perm;
const char *p, *dir;
int fd;
/* figure out what permissions we should use for maildirsize.
use the inbox namespace's permissions if possible. */
for (i = 0; i < count; i++) {
continue;
&perm);
break;
}
/* the control directory doesn't exist yet? create it */
perm.file_create_gid_origin) < 0 &&
return -1;
}
}
if (fd == -1) {
return -1;
}
/* if we have no limits, write 0S instead of an empty line */
}
if (_root->count_limit != 0) {
}
i_close_fd(&fd);
return -1;
}
i_close_fd(&fd);
return -1;
}
return 0;
}
{
root->recalc_last_stamp = 0;
}
struct mail_namespace *ns)
{
struct maildir_list_context *ctx;
const char *dir;
int ret = 0;
&root->total_count) < 0)
ret = -1;
}
if (maildir_list_deinit(ctx) < 0)
ret = -1;
return ret;
}
{
/* FIXME: can't unlink(), because the limits would be lost. */
return;
}
}
int ret)
{
if (ret == 0) {
/* maildir didn't change, we can write the maildirsize file */
}
if (ret != 0)
return ret;
}
{
struct mail_namespace *const *namespaces;
unsigned int i, count;
int ret = 0;
/* count mails from all namespaces */
for (i = 0; i < count; i++) {
continue;
ret = -1;
break;
}
}
if (ret == 0) {
/* check if any of the directories have changed */
for (i = 0; i < count; i++) {
namespaces[i]))
continue;
if (ret != 0)
break;
}
}
}
static bool
{
const char *const *limit;
unsigned long long value;
const char *pos;
*bytes_r = 0;
*count_r = 0;
/* 0 values mean unlimited */
continue;
}
switch (pos[0]) {
case 'C':
if (value != 0)
break;
case 'S':
if (value != 0)
break;
default:
break;
}
} else {
}
}
return ret;
}
{
long long bytes_diff, total_bytes;
int count_diff, total_count;
unsigned int line_count = 0;
return -1;
/* first line contains the limits */
/* truncate too high limits to signed 64bit int range */
/* limits haven't changed */
/* we know the limits and they've changed.
the file must be rewritten. */
return 0;
} else {
/* we're using limits from the file. */
}
/* no quota lines. rebuild it. */
return 0;
}
/* rest of the lines contains <bytes> <count> diffs */
total_bytes = 0; total_count = 0;
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;
}
{
return 0;
return -1;
}
return 1;
}
{
if (dotlock_settings.nfs_flush) {
}
return TRUE;
return TRUE;
return TRUE;
}
{
unsigned int i, size;
bool retry_estale = *retry;
int ret;
if (!maildirsize_has_changed(root))
return 1;
return ret;
/* @UNSAFE */
size = 0;
if (ret < 0) {
break;
}
break;
}
/* we'll need to recalculate the quota */
break;
}
}
/* try to use the file even if we ran into some error. if we don't have
forced limits, we'll need to read the header to get them */
/* 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. */
/* the read failed and there's no usable header, fail. */
return -1;
}
/* 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 == 0)
ret = 1;
else {
/* broken file / need recalculation */
ret = 0;
}
return ret;
}
{
struct mailbox_list *list;
struct mail_storage *storage;
const char *control_dir;
if (root->limits_initialized)
return FALSE;
}
/* non-maildir namespace, skip */
if ((storage->class_flags &
MAIL_STORAGE_CLASS_FLAG_NOQUOTA) == 0) {
i_warning("quota: Namespace '%s' is not Maildir, "
"skipping for Maildir++ quota",
}
return FALSE;
}
&control_dir))
i_unreached();
}
return TRUE;
}
{
int ret, n = 0;
if (!maildirquota_limits_init(root))
return 1;
do {
if (n == NFS_ESTALE_RETRY_COUNT)
n++;
return ret;
}
static int
{
int ret;
*recalculated_r = FALSE;
if (ret == 0) {
/* no quota */
return 0;
/* explicitly specified 0 as quota. keep the quota
updated even if it's not enforced. */
}
if (ret == 0)
*recalculated_r = TRUE;
}
return ret < 0 ? -1 : 0;
}
{
int ret = 0;
if (count_diff == 0 && bytes_diff == 0)
return 0;
/* 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 causes calculations to go
over quota. This is also how Maildir++ spec specifies it should be
done.. */
(long long)bytes_diff, count_diff) < 0)
i_unreached();
ret = -1;
} else {
i_error("write_full(%s) failed: %m",
}
} else {
/* close the file to force a flush with NFS */
ret = -1;
}
}
return ret;
}
static struct quota_root *maildir_quota_alloc(void)
{
struct maildir_quota_root *root;
}
const char **error_r)
{
}
{
}
static bool
struct quota_rule *rule,
{
bytes = 0;
count = 0;
*error_r = "Invalid Maildir++ quota rule";
return FALSE;
}
return TRUE;
}
const char **error_r)
{
if (maildirquota_read_limits(root) < 0) {
*error_r = "Failed to read maildir quota limits";
return -1;
}
return 0;
}
static void
struct mail_namespace *ns)
{
}
static void
{
struct quota_root **roots;
unsigned int i, count;
for (i = 0; i < count; i++) {
}
}
static const char *const *
{
static const char *resources_both[] = {
};
return resources_both;
}
static int
{
bool recalculated;
*error_r = "quota-maildir failed";
return -1;
}
} else
return 0;
return 1;
}
static int
struct quota_transaction_context *ctx,
const char **error_r)
{
bool recalculated;
if (!maildirquota_limits_init(root)) {
/* no limits */
return 0;
}
/* even though we don't really care about the limits in here ourself,
we do want to make sure the header gets updated if the limits have
changed. also this makes sure the maildirsize file is created if
it doesn't exist. */
*error_r = "Failed to refresh maildir quota";
return -1;
}
if (recalculated) {
/* quota was just recalculated and it already contains the changes
we wanted to do. */
(void)maildirsize_recalculate(root);
(void)maildirsize_recalculate(root);
}
return 0;
}
struct quota_backend quota_backend_maildir = {
.name = "maildir",
.v = {
}
};