maildir-uidlist.c revision ff2d8bbbbc4f53d20bd54c714f35febc7bcb8499
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* NFS: How many times to retry reading dovecot-uidlist file if ESTALE
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen error occurs in the middle of reading it */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* how many seconds to wait before overriding uidlist.lock */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int version;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int uid_validity, next_uid, prev_read_uid, last_seen_uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct maildir_uidlist_rec *const *next, *const *end;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen path = t_strconcat(mbox->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen old_mask = umask(0777 & ~mbox->mail_create_mode);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen fd = file_dotlock_open(&uidlist->dotlock_settings, path,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_error(&mbox->storage->storage,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen mail_storage_set_critical(&mbox->storage->storage,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (fchown(fd, (uid_t)-1, mbox->mail_create_gid) < 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen mail_storage_set_critical(&mbox->storage->storage,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* our view of uidlist must be up-to-date if we plan on changing it */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenint maildir_uidlist_lock(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return maildir_uidlist_lock_timeout(uidlist, FALSE);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenint maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return maildir_uidlist_lock_timeout(uidlist, TRUE);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenint maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenbool maildir_uidlist_is_locked(struct maildir_uidlist *uidlist)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenvoid maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (file_dotlock_replace(&uidlist->dotlock, 0) <= 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen db_path = t_strconcat(uidlist->mbox->control_dir,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "file_dotlock_replace(%s) failed: %m", db_path);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstruct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_strconcat(mbox->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->record_buf = buffer_create_dynamic(default_pool, 512);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->files = hash_create(default_pool, default_pool, 4096,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->dotlock_settings.timeout = UIDLIST_LOCK_STALE_TIMEOUT + 2;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->dotlock_settings.stale_timeout = UIDLIST_LOCK_STALE_TIMEOUT;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->dotlock_settings.temp_prefix = mbox->storage->temp_prefix;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenvoid maildir_uidlist_deinit(struct maildir_uidlist *uidlist)
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainenmaildir_uidlist_mark_recent(struct maildir_uidlist *uidlist, uint32_t uid)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstatic int maildir_uidlist_next(struct maildir_uidlist *uidlist,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* invalid file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen mail_storage_set_critical(&uidlist->mbox->storage->storage,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen mail_storage_set_critical(&uidlist->mbox->storage->storage,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "UIDs not ordered in file %s (%u > %u)",
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* we already have this */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(&uidlist->mbox->storage->storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "UID larger than next_uid in file %s (%u >= %u)",
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen /* skip flags parameter */
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen if (hash_lookup_full(uidlist->files, line, NULL, NULL)) {
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen mail_storage_set_critical(&uidlist->mbox->storage->storage,
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen "Duplicate file in uidlist file %s: %s",
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen rec->flags = MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen rec->filename = p_strdup(uidlist->record_pool, line);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen hash_insert(uidlist->files, rec->filename, rec);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen buffer_append(uidlist->record_buf, &rec, sizeof(rec));
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenmaildir_uidlist_update_read(struct maildir_uidlist *uidlist,
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen struct mail_storage *storage = &uidlist->mbox->storage->storage;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "uidlist record_pool",
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen input = i_stream_create_file(fd, default_pool, 4096, TRUE);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* get header */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* I/O error / empty file */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen } else if (sscanf(line, "%u %u %u", &uidlist->version,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->version < 1 || uidlist->version > 2) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* broken file */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen "Corrupted header in file %s (version = %u)",
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen } else if (uid_validity == uidlist->uid_validity &&
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen "%s: next_uid was lowered (%u -> %u)",
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen } else if (uid_validity == 0 || next_uid == 0) {
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen "%s: Broken header (uidvalidity = %u, next_uid=%u)",
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* file is broken */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen } else if (ret > 0) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* success */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* I/O error */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (input->stream_errno == ESTALE && try_retry)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenint maildir_uidlist_update(struct maildir_uidlist *uidlist)
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen struct mail_storage *storage = &uidlist->mbox->storage->storage;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int i;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* unchanged */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen for (i = 0; ; i++) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen ret = maildir_uidlist_update_read(uidlist, &retry,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* ESTALE - try reopening and rereading */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainenstatic const struct maildir_uidlist_rec *
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenmaildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int *idx_r)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen const struct maildir_uidlist_rec *const *rec_p;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* first time we need to read uidlist */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen rec_p = buffer_get_data(uidlist->record_buf, &size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmaildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int idx;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* the uidlist doesn't exist. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (maildir_storage_sync_force(uidlist->mbox) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* try again */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenbool maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid)
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen if (uidlist->first_recent_uid == 0 || uid < uidlist->first_recent_uid)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (maildir_uidlist_lookup(uidlist, uid, &flags) == NULL)
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenuint32_t maildir_uidlist_get_recent_count(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen const struct maildir_uidlist_rec *const *rec_p;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int idx;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* we haven't synced yet, trust index */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen hdr = mail_index_get_header(uidlist->mbox->ibox.view);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* all recent messages were in new/ dir, so even if we did only
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen a partial sync we should know all the recent messages. */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen rec_p = buffer_get_data(uidlist->record_buf, &size);
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen maildir_uidlist_lookup_rec(uidlist, uidlist->first_recent_uid, &idx);
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen if ((rec_p[idx]->flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainenuint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist)
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainenvoid maildir_uidlist_set_uid_validity(struct maildir_uidlist *uidlist,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* set next_uid only if we know newer UIDs haven't been added yet */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenuint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return !uidlist->initial_read ? 0 : uidlist->next_uid;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstatic int maildir_uidlist_rewrite_fd(struct maildir_uidlist *uidlist,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct mail_storage *storage = &uidlist->mbox->storage->storage;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen /* already written, truncate */
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen if (lseek(uidlist->lock_fd, 0, SEEK_SET) < 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* Get UIDVALIDITY from index */
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen hdr = mail_index_get_header(uidlist->mbox->ibox.view);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen str_printfa(str, "%u %u %u\n", uidlist->version,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen iter = maildir_uidlist_iter_init(uidlist->mbox->uidlist);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen while (maildir_uidlist_iter_next(iter, &uid, &flags, &filename)) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* avoid overflowing str buffer so we don't eat more memory
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen than we need. */
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* flush buffer */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (write_full(uidlist->lock_fd, str_data(str), str_len(str)) < 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* uidlist's mtime must grow every time */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->last_mtime = ioloop_time <= uidlist->last_mtime ?
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainenstatic int maildir_uidlist_rewrite(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen 1 + (uidlist->mbox->ibox.keep_locked ? 1 : 0));
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen ret = maildir_uidlist_rewrite_fd(uidlist, temp_path);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (ret == 0 && !uidlist->mbox->ibox.keep_locked) {
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen if (file_dotlock_replace(&uidlist->dotlock, 0) <= 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(&mbox->storage->storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "file_dotlock_replace(%s) failed: %m", db_path);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstatic void maildir_uidlist_mark_all(struct maildir_uidlist *uidlist,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec_p = buffer_get_modifiable_data(uidlist->record_buf, &size);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen for (i = 0; i < size; i++)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen rec_p[i]->flags |= MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
for (i = 0; i < size; i++)
int ret;
return ret;
const char *filename,
sizeof(rec);
const char *filename)
const char *filename,
MAILDIR_UIDLIST_REC_FLAG_MOVED)) == 0) {
const char *filename)
const char *filename)
s1++;
s2++;
unsigned int first_new_pos)
unsigned int dest;
MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0) {
t_push();
t_pop();
if (ret == 0)
if (!unlocked)
return ret;
const char *filename,
struct maildir_uidlist_iter_ctx *
return ctx;
const char **filename_r)