maildir-uidlist.c revision 892f02d4ab8f764f86015009aaf7437349398286
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "lib.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "ioloop.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "istream.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "str.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "write-full.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "mail-index.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "mail-index-util.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "maildir-index.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include "maildir-uidlist.h"
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include <stdio.h>
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include <sys/stat.h>
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#include <utime.h>
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen/* how many seconds to wait before overriding uidlist.lock */
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen#define UIDLIST_LOCK_STALE_TIMEOUT (60*5)
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainenint maildir_uidlist_try_lock(struct mail_index *index)
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen{
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen const char *path;
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen int fd;
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen if (INDEX_IS_UIDLIST_LOCKED(index))
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen return 1;
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen path = t_strconcat(index->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen fd = file_dotlock_open(path, NULL, 0, UIDLIST_LOCK_STALE_TIMEOUT,
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen NULL, NULL);
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen if (fd == -1) {
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen if (errno == EAGAIN)
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen return 0;
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen return -1;
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen }
5f08b0309190ec818d46bfe0e497468b30714a93Timo Sirainen
index->maildir_lock_fd = fd;
return 1;
}
void maildir_uidlist_unlock(struct mail_index *index)
{
const char *path;
if (!INDEX_IS_UIDLIST_LOCKED(index))
return;
path = t_strconcat(index->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
(void)file_dotlock_delete(path, index->maildir_lock_fd);
index->maildir_lock_fd = -1;
}
struct maildir_uidlist *maildir_uidlist_open(struct mail_index *index)
{
const char *path, *line;
struct maildir_uidlist *uidlist;
unsigned int version;
int fd;
path = t_strconcat(index->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
fd = open(path, O_RDONLY);
if (fd == -1) {
if (errno != ENOENT)
index_file_set_syscall_error(index, path, "open()");
return NULL;
}
uidlist = i_new(struct maildir_uidlist, 1);
uidlist->index = index;
uidlist->fname = i_strdup(path);
uidlist->input = i_stream_create_file(fd, default_pool, 4096, TRUE);
/* get header */
line = i_stream_read_next_line(uidlist->input);
if (line == NULL || sscanf(line, "%u %u %u", &version,
&uidlist->uid_validity,
&uidlist->next_uid) != 3 ||
version != 1) {
/* broken file */
(void)unlink(path);
maildir_uidlist_close(uidlist);
return NULL;
}
return uidlist;
}
int maildir_uidlist_next(struct maildir_uidlist *uidlist,
struct maildir_uidlist_rec *uid_rec)
{
const char *line;
unsigned int uid;
memset(uid_rec, 0, sizeof(*uid_rec));
line = i_stream_read_next_line(uidlist->input);
if (line == NULL)
return 0;
uid = 0;
while (*line >= '0' && *line <= '9') {
uid = uid*10 + (*line - '0');
line++;
}
if (uid == 0 || *line != ' ') {
/* invalid file */
index_set_error(uidlist->index, "Invalid data in file %s",
uidlist->fname);
(void)unlink(uidlist->fname);
return -1;
}
if (uid <= uidlist->last_read_uid) {
index_set_error(uidlist->index,
"UIDs not ordered in file %s (%u > %u)",
uidlist->fname, uid, uidlist->last_read_uid);
(void)unlink(uidlist->fname);
return -1;
}
if (uid >= uidlist->next_uid) {
index_set_error(uidlist->index,
"UID larger than next_uid in file %s "
"(%u >= %u)", uidlist->fname,
uid, uidlist->next_uid);
(void)unlink(uidlist->fname);
return -1;
}
while (*line == ' ') line++;
uid_rec->uid = uid;
uid_rec->filename = line;
return 1;
}
void maildir_uidlist_close(struct maildir_uidlist *uidlist)
{
i_stream_unref(uidlist->input);
i_free(uidlist->fname);
i_free(uidlist);
}
static int maildir_uidlist_rewrite_fd(struct mail_index *index,
const char *temp_path, time_t *mtime)
{
struct mail_index_record *rec;
struct utimbuf ut;
const char *p, *fname;
string_t *str;
size_t len;
str = t_str_new(4096);
str_printfa(str, "1 %u %u\n",
index->header->uid_validity, index->header->next_uid);
rec = index->lookup(index, 1);
while (rec != NULL) {
fname = maildir_get_location(index, rec);
if (fname == NULL)
return FALSE;
p = strchr(fname, ':');
len = p == NULL ? strlen(fname) : (size_t)(p-fname);
if (str_len(str) + MAX_INT_STRLEN + len + 2 >= 4096) {
/* flush buffer */
if (write_full(index->maildir_lock_fd,
str_data(str), str_len(str)) < 0) {
index_file_set_syscall_error(index, temp_path,
"write_full()");
return FALSE;
}
str_truncate(str, 0);
}
str_printfa(str, "%u ", rec->uid);
str_append_n(str, fname, len);
str_append_c(str, '\n');
rec = index->next(index, rec);
}
if (write_full(index->maildir_lock_fd,
str_data(str), str_len(str)) < 0) {
index_file_set_syscall_error(index, temp_path, "write_full()");
return FALSE;
}
/* uidlist's mtime must grow every time */
*mtime = ioloop_time > *mtime ? ioloop_time : *mtime + 1;
ut.actime = ioloop_time;
ut.modtime = *mtime;
if (utime(temp_path, &ut) < 0)
index_set_syscall_error(index, "utime()");
if (fsync(index->maildir_lock_fd) < 0) {
index_file_set_syscall_error(index, temp_path, "fsync()");
return FALSE;
}
return TRUE;
}
int maildir_uidlist_rewrite(struct mail_index *index, time_t *mtime)
{
const char *temp_path, *db_path;
int failed = FALSE;
i_assert(INDEX_IS_UIDLIST_LOCKED(index));
if (index->lock_type == MAIL_LOCK_UNLOCK) {
if (!index->set_lock(index, MAIL_LOCK_SHARED))
return FALSE;
}
temp_path = t_strconcat(index->control_dir,
"/" MAILDIR_UIDLIST_NAME ".lock", NULL);
failed = !maildir_uidlist_rewrite_fd(index, temp_path, mtime);
if (!failed) {
db_path = t_strconcat(index->control_dir,
"/" MAILDIR_UIDLIST_NAME, NULL);
if (file_dotlock_replace(db_path, index->maildir_lock_fd,
FALSE) <= 0) {
index_set_error(index,
"file_dotlock_replace(%s) failed: %m",
db_path);
failed = TRUE;
}
} else {
(void)close(index->maildir_lock_fd);
}
index->maildir_lock_fd = -1;
if (failed)
(void)unlink(temp_path);
return !failed;
}