maildir-uidlist.c revision ee1681a053a515048d82fb57c366c26696fa0a33
/* Copyright (C) 2003 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "array.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-sync.h"
#include "maildir-uidlist.h"
#include <stdio.h>
#include <stdlib.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 fd;
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) {
return 0;
}
"file_dotlock_open(%s) failed: %m", path);
return -1;
}
"fchown(%s) failed: %m", path);
}
}
/* 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;
}
"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 if (uid_validity == 0 || next_uid == 0) {
"%s: Broken header (uidvalidity = %u, next_uid=%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 */
} else if (ret > 0) {
/* success */
} else {
/* I/O error */
else {
}
}
if (ret <= 0) {
}
return ret;
}
{
unsigned int i;
bool retry;
int ret;
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)
{
struct maildir_uidlist_rec *const *recs;
if (!uidlist->initial_read) {
/* first time we need to read uidlist */
if (maildir_uidlist_update(uidlist) < 0)
return NULL;
}
else {
}
}
return NULL;
}
const char *
enum maildir_uidlist_rec_flag *flags_r)
{
const struct maildir_uidlist_rec *rec;
unsigned int idx;
return NULL;
/* the uidlist doesn't exist. */
return NULL;
/* try again */
return NULL;
}
}
{
return FALSE;
return FALSE;
(flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
}
{
struct maildir_uidlist_rec *const *recs;
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;
recent_count++;
}
return recent_count;
}
{
return uidlist->uid_validity;
}
{
/* set next_uid only if we know newer UIDs haven't been added yet */
}
{
}
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;
}
"fsync(%s) failed: %m", temp_path);
return -1;
}
}
return 0;
}
{
int ret;
DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) <= 0) {
"file_dotlock_replace(%s) failed: %m", db_path);
ret = -1;
} else {
} else {
}
}
uidlist->lock_count--;
} else {
}
return ret;
}
bool nonsynced)
{
struct maildir_uidlist_rec **recs;
unsigned int i, count;
if (nonsynced) {
for (i = 0; i < count; i++)
} else {
for (i = 0; i < count; i++)
}
}
struct maildir_uidlist_sync_ctx **sync_ctx_r)
{
struct maildir_uidlist_sync_ctx *ctx;
int ret;
return ret;
/* initially mark all nonsynced */
return 1;
}
"maildir_uidlist_sync", 16384);
return 1;
}
static int
const char *filename,
{
struct maildir_uidlist_rec *rec;
/* we'll update uidlist directly */
if (ctx->new_files_count == 0)
ctx->new_files_count++;
"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;
}
const char *
const char *filename)
{
struct maildir_uidlist_rec *rec;
}
const char *
const char *filename)
{
struct maildir_uidlist_rec *rec;
}
{
/* 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 **recs;
/* sort new files and assign UIDs for them */
sizeof(*recs), maildir_time_cmp);
}
MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0) {
}
}
}
{
}
{
struct maildir_uidlist_rec **recs;
unsigned int count;
/* buffer is unsorted, sort it by UID */
if (ctx->new_files_count != 0)
}
{
/* saving a message to a newly created maildir */
}
} else {
if (ctx->new_files_count != 0)
}
}
{
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;
unsigned int count;
return ctx;
}
enum maildir_uidlist_rec_flag *flags_r,
const char **filename_r)
{
return 0;
return 1;
}
{
}