maildir-uidlist.c revision e05181d973025627ba08b631c12c07c3bbc99528
/* Copyright (C) 2003 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "hash.h"
#include "istream.h"
#include "str.h"
#include "file-dotlock.h"
#include "close-keep-errno.h"
#include "nfs-workarounds.h"
#include "write-full.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <utime.h>
/* NFS: How many times to retry reading dovecot-uidlist file if ESTALE
error occurs in the middle of reading it */
/* how many seconds to wait before overriding uidlist.lock */
#define UIDLIST_IS_LOCKED(uidlist) \
((uidlist)->lock_count > 0)
struct maildir_uidlist_rec {
char *filename;
};
struct maildir_uidlist {
struct maildir_mailbox *mbox;
char *fname;
int lock_fd;
unsigned int lock_count;
struct hash_table *files;
struct dotlock_settings dotlock_settings;
unsigned int version;
unsigned int initial_read:1;
unsigned int initial_sync:1;
unsigned int need_rewrite:1;
unsigned int delayed_rewrite:1;
};
struct maildir_uidlist_sync_ctx {
struct maildir_uidlist *uidlist;
struct hash_table *files;
unsigned int first_new_pos;
unsigned int new_files_count;
unsigned int partial:1;
unsigned int finished:1;
unsigned int failed:1;
};
struct maildir_uidlist_iter_ctx {
};
bool nonblock)
{
const char *path;
int fd;
if (uidlist->lock_count > 0) {
uidlist->lock_count++;
return 1;
}
if (fd == -1) {
"Timeout while waiting for lock");
return 0;
}
"file_dotlock_open(%s) failed: %m", path);
return -1;
}
/* our view of uidlist must be up-to-date if we plan on changing it */
if (maildir_uidlist_update(uidlist) < 0)
return -1;
uidlist->lock_count++;
return 1;
}
{
}
{
}
{
return UIDLIST_IS_LOCKED(uidlist);
}
{
if (--uidlist->lock_count > 0)
return;
if (!uidlist->delayed_rewrite) {
} else {
const char *db_path;
"file_dotlock_replace(%s) failed: %m", db_path);
}
}
}
{
struct maildir_uidlist *uidlist;
return uidlist;
}
{
}
static void
{
if (uidlist->first_recent_uid == 0 ||
}
const char *line)
{
struct maildir_uidlist_rec *rec;
line++;
}
/* invalid file */
return 0;
}
"UIDs not ordered in file %s (%u > %u)",
return 0;
}
/* we already have this */
return 1;
}
"UID larger than next_uid in file %s (%u >= %u)",
return 0;
}
/* skip flags parameter */
}
"Duplicate file in uidlist file %s: %s",
return 0;
}
return 1;
}
static int
{
const char *line;
unsigned int uid_validity, next_uid;
if (fd == -1) {
return -1;
}
return 0;
}
return -1;
}
return -1;
}
pool_alloconly_create("uidlist record_pool",
}
/* get header */
/* I/O error / empty file */
/* broken file */
"Corrupted header in file %s (version = %u)",
ret = 0;
"%s: next_uid was lowered (%u -> %u)",
ret = 0;
} else {
uidlist->prev_read_uid = 0;
ret = 1;
ret = 0;
break;
}
}
if (input->stream_errno != 0)
ret = -1;
}
if (ret == 0) {
/* file is broken */
uidlist->last_mtime = 0;
} else if (ret > 0) {
/* success */
} else {
/* I/O error */
else {
}
}
return ret;
}
{
unsigned int i;
bool retry;
int ret;
if (uidlist->last_mtime != 0) {
return -1;
}
return 0;
}
/* unchanged */
return 1;
}
}
for (i = 0; ; i++) {
if (!retry) {
if (ret >= 0)
break;
}
/* ESTALE - try reopening and rereading */
}
return ret;
}
static const struct maildir_uidlist_rec *
unsigned int *idx_r)
{
const struct maildir_uidlist_rec *const *rec_p;
if (!uidlist->initial_read) {
/* first time we need to read uidlist */
if (maildir_uidlist_update(uidlist) < 0)
return NULL;
}
idx = 0;
left_idx = 0;
else {
}
}
return NULL;
}
const char *
enum maildir_uidlist_rec_flag *flags_r)
{
const struct maildir_uidlist_rec *rec;
unsigned int idx;
return NULL;
}
{
return FALSE;
return FALSE;
(flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
}
{
const struct maildir_uidlist_rec *const *rec_p;
unsigned int idx;
if (!uidlist->initial_sync) {
/* we haven't synced yet, trust index */
const struct mail_index_header *hdr;
return hdr->recent_messages_count;
}
/* all recent messages were in new/ dir, so even if we did only
a partial sync we should know all the recent messages. */
if (uidlist->first_recent_uid == 0)
return 0;
count++;
}
return count;
}
{
return uidlist->uid_validity;
}
{
}
{
}
const char *temp_path)
{
struct maildir_uidlist_iter_ctx *iter;
const char *filename;
int ret = 0;
if (uidlist->delayed_rewrite) {
/* already written, truncate */
"lseek(%s) failed: %m", temp_path);
return -1;
}
"ftruncate(%s) failed: %m", temp_path);
return -1;
}
}
if (uidlist->uid_validity == 0) {
/* Get UIDVALIDITY from index */
const struct mail_index_header *hdr;
}
/* avoid overflowing str buffer so we don't eat more memory
than we need. */
/* flush buffer */
"write_full(%s) failed: %m", temp_path);
ret = -1;
break;
}
str_truncate(str, 0);
}
}
if (ret < 0)
return -1;
"write_full(%s) failed: %m", temp_path);
return -1;
}
/* uidlist's mtime must grow every time */
"utime(%s) failed: %m", temp_path);
return -1;
}
"fsync(%s) failed: %m", temp_path);
return -1;
}
return 0;
}
{
int ret;
"file_dotlock_replace(%s) failed: %m", db_path);
ret = -1;
}
uidlist->lock_count--;
} else {
}
return ret;
}
bool nonsynced)
{
struct maildir_uidlist_rec **rec_p;
if (nonsynced) {
for (i = 0; i < size; i++)
} else {
for (i = 0; i < size; i++)
}
}
struct maildir_uidlist_sync_ctx **sync_ctx_r)
{
struct maildir_uidlist_sync_ctx *ctx;
int ret;
return ret;
if (partial) {
/* initially mark all nonsynced */
return 1;
}
ctx->record_pool =
return 1;
}
static int
const char *filename,
{
struct maildir_uidlist_rec *rec;
/* we'll update uidlist directly */
if (ctx->new_files_count == 0) {
ctx->first_new_pos =
sizeof(rec);
}
ctx->new_files_count++;
pool_alloconly_create("uidlist record_pool",
1024);
}
struct maildir_uidlist_rec, 1);
}
if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
return 1;
}
const char *filename)
{
/* first time reading the uidlist */
return -1;
}
}
return 0;
}
return 1;
}
const char *filename,
{
return -1;
MAILDIR_UIDLIST_REC_FLAG_MOVED)) == 0) {
/* possibly duplicate */
return 0;
}
} else {
else {
ctx->new_files_count++;
}
}
if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
return 1;
}
{
/* we have to do numeric comparision, strcmp() will break when
there's different amount of digits (mostly the 999999999 ->
1000000000 change in Sep 9 2001) */
s1++;
}
s2++;
}
}
unsigned int first_new_pos)
{
struct maildir_uidlist_rec **rec_p;
unsigned int dest;
/* sort new files and assign UIDs for them */
sizeof(*rec_p), maildir_time_cmp);
}
}
{
}
{
struct maildir_uidlist_rec **rec_p;
/* buffer is unsorted, sort it by UID */
if (ctx->new_files_count != 0) {
}
}
{
} else {
if (ctx->new_files_count != 0) {
ctx->first_new_pos);
}
}
}
{
unsigned int nonrecursive_lock_count = 1;
/* recursive sync. let the root syncing do
the rewrite */
} else {
t_push();
t_pop();
if (ret == 0)
}
}
if (!unlocked)
return ret;
}
const char *filename,
{
struct maildir_uidlist_rec *rec;
}
struct maildir_uidlist_iter_ctx *
{
struct maildir_uidlist_iter_ctx *ctx;
return ctx;
}
enum maildir_uidlist_rec_flag *flags_r,
const char **filename_r)
{
return 0;
return 1;
}
{
}