maildir-uidlist.c revision ff2d8bbbbc4f53d20bd54c714f35febc7bcb8499
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ioloop.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "buffer.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hash.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "file-dotlock.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "close-keep-errno.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "nfs-workarounds.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "write-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-storage.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-sync.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-uidlist.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <stdio.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <stdlib.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <sys/stat.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <utime.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo 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
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* how many seconds to wait before overriding uidlist.lock */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_LOCK_STALE_TIMEOUT (60*2)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_IS_LOCKED(uidlist) \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ((uidlist)->lock_count > 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct maildir_uidlist_rec {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t flags;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen char *filename;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct maildir_uidlist {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_mailbox *mbox;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen char *fname;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int lock_fd;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int lock_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen time_t last_mtime;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pool_t record_pool;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen buffer_t *record_buf;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct hash_table *files;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dotlock_settings dotlock_settings;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dotlock *dotlock;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int version;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int uid_validity, next_uid, prev_read_uid, last_seen_uid;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen uint32_t first_recent_uid;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int initial_read:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int initial_sync:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen unsigned int need_rewrite:1;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen unsigned int delayed_rewrite:1;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenstruct maildir_uidlist_sync_ctx {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_uidlist *uidlist;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen enum maildir_uidlist_sync_flags sync_flags;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pool_t record_pool;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_t *record_buf;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct hash_table *files;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int first_new_pos;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int new_files_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int partial:1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int finished:1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int failed:1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen};
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenstruct maildir_uidlist_iter_ctx {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct maildir_uidlist_rec *const *next, *const *end;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen bool nonblock)
659fe5d24825b160cae512538088020d97a60239Timo Sirainen{
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen struct maildir_mailbox *mbox = uidlist->mbox;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen const char *path;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen mode_t old_mask;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen int fd;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (uidlist->lock_count > 0) {
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen uidlist->lock_count++;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return 1;
659fe5d24825b160cae512538088020d97a60239Timo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
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,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen nonblock ? DOTLOCK_CREATE_FLAG_NONBLOCK : 0,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen &uidlist->dotlock);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen umask(old_mask);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (fd == -1) {
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (errno == EAGAIN) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_error(&mbox->storage->storage,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return 0;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen mail_storage_set_critical(&mbox->storage->storage,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen "file_dotlock_open(%s) failed: %m", path);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return -1;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen uidlist->lock_fd = fd;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (mbox->mail_create_gid != (gid_t)-1) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (fchown(fd, (uid_t)-1, mbox->mail_create_gid) < 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen mail_storage_set_critical(&mbox->storage->storage,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen "fchown(%s) failed: %m", path);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen }
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* our view of uidlist must be up-to-date if we plan on changing it */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (maildir_uidlist_update(uidlist) < 0)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return -1;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen uidlist->lock_count++;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return 1;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen}
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenint maildir_uidlist_lock(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return maildir_uidlist_lock_timeout(uidlist, FALSE);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenint maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen{
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return maildir_uidlist_lock_timeout(uidlist, TRUE);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenint maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen i_assert(UIDLIST_IS_LOCKED(uidlist));
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return file_dotlock_touch(uidlist->dotlock);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenbool maildir_uidlist_is_locked(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return UIDLIST_IS_LOCKED(uidlist);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenvoid maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_assert(uidlist->lock_count > 0);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (--uidlist->lock_count > 0)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (!uidlist->delayed_rewrite) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen (void)file_dotlock_delete(&uidlist->dotlock);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen } else {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (file_dotlock_replace(&uidlist->dotlock, 0) <= 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen const char *db_path;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen db_path = t_strconcat(uidlist->mbox->control_dir,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "/" MAILDIR_UIDLIST_NAME, NULL);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen mail_storage_set_critical(
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen &uidlist->mbox->storage->storage,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "file_dotlock_replace(%s) failed: %m", db_path);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->delayed_rewrite = FALSE;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->lock_fd = -1;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen}
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstruct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen struct maildir_uidlist *uidlist;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist = i_new(struct maildir_uidlist, 1);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->mbox = mbox;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->fname =
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_strconcat(mbox->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->lock_fd = -1;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->record_buf = buffer_create_dynamic(default_pool, 512);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->files = hash_create(default_pool, default_pool, 4096,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen maildir_hash, maildir_cmp);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->next_uid = 1;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->dotlock_settings.use_excl_lock =
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen (mbox->storage->storage.flags &
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0;
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 Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return uidlist;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen}
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenvoid maildir_uidlist_deinit(struct maildir_uidlist *uidlist)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_assert(!UIDLIST_IS_LOCKED(uidlist));
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen hash_destroy(uidlist->files);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (uidlist->record_pool != NULL)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen pool_unref(uidlist->record_pool);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen buffer_free(uidlist->record_buf);
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen i_free(uidlist->fname);
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen i_free(uidlist);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainenmaildir_uidlist_mark_recent(struct maildir_uidlist *uidlist, uint32_t uid)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (uidlist->first_recent_uid == 0 ||
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen uid < uidlist->first_recent_uid)
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen uidlist->first_recent_uid = uid;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstatic int maildir_uidlist_next(struct maildir_uidlist *uidlist,
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen const char *line)
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen struct maildir_uidlist_rec *rec;
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen uint32_t uid, flags;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen uid = flags = 0;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen while (*line >= '0' && *line <= '9') {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen uid = uid*10 + (*line - '0');
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen line++;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (uid == 0 || *line != ' ') {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* invalid file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen mail_storage_set_critical(&uidlist->mbox->storage->storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Invalid data in file %s", uidlist->fname);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return 0;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (uid <= uidlist->prev_read_uid) {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen mail_storage_set_critical(&uidlist->mbox->storage->storage,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "UIDs not ordered in file %s (%u > %u)",
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen uidlist->fname, uid, uidlist->prev_read_uid);
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->prev_read_uid = uid;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (uid <= uidlist->last_seen_uid) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* we already have this */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return 1;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen uidlist->last_seen_uid = uid;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen if (uid >= uidlist->next_uid) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(&uidlist->mbox->storage->storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "UID larger than next_uid in file %s (%u >= %u)",
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->fname, uid, uidlist->next_uid);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return 0;
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
659fe5d24825b160cae512538088020d97a60239Timo Sirainen while (*line == ' ') line++;
659fe5d24825b160cae512538088020d97a60239Timo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (uidlist->version == 2) {
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen /* skip flags parameter */
659fe5d24825b160cae512538088020d97a60239Timo Sirainen while (*line != ' ') line++;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen while (*line == ' ') line++;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen }
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen
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 uidlist->fname, line);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return 0;
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen rec->uid = uid;
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));
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen return 1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenstatic int
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenmaildir_uidlist_update_read(struct maildir_uidlist *uidlist,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen bool *retry_r, bool try_retry)
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen{
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen struct mail_storage *storage = &uidlist->mbox->storage->storage;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen const char *line;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int uid_validity, next_uid;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen struct istream *input;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen struct stat st;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen int fd, ret;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen *retry_r = FALSE;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen fd = nfs_safe_open(uidlist->fname, O_RDONLY);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (fd == -1) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (errno != ENOENT) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "open(%s) failed: %m", uidlist->fname);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return -1;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen return 0;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (fstat(fd, &st) < 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen close_keep_errno(fd);
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen if (errno == ESTALE && try_retry) {
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen *retry_r = TRUE;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen return -1;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen mail_storage_set_critical(storage,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "fstat(%s) failed: %m", uidlist->fname);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return -1;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (uidlist->record_pool == NULL) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->record_pool =
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen pool_alloconly_create(MEMPOOL_GROWING
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "uidlist record_pool",
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen nearest_power(st.st_size -
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen st.st_size/8));
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->version = 0;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen input = i_stream_create_file(fd, default_pool, 4096, TRUE);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* get header */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen line = i_stream_read_next_line(input);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (line == NULL) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* I/O error / empty file */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen ret = input->stream_errno == 0 ? 0 : -1;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen } else if (sscanf(line, "%u %u %u", &uidlist->version,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen &uid_validity, &next_uid) != 3 ||
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen uidlist->version < 1 || uidlist->version > 2) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* broken file */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen mail_storage_set_critical(storage,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen "Corrupted header in file %s (version = %u)",
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen uidlist->fname, uidlist->version);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen ret = 0;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen } else if (uid_validity == uidlist->uid_validity &&
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen next_uid < uidlist->next_uid) {
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen mail_storage_set_critical(storage,
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen "%s: next_uid was lowered (%u -> %u)",
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen uidlist->fname, uidlist->next_uid, next_uid);
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen ret = 0;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen } else if (uid_validity == 0 || next_uid == 0) {
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen mail_storage_set_critical(storage,
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen "%s: Broken header (uidvalidity = %u, next_uid=%u)",
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen uidlist->fname, uid_validity, next_uid);
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen ret = 0;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen } else {
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen uidlist->uid_validity = uid_validity;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen uidlist->next_uid = next_uid;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen uidlist->prev_read_uid = 0;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen ret = 1;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (!maildir_uidlist_next(uidlist, line)) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen ret = 0;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen break;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (input->stream_errno != 0)
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen ret = -1;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (ret == 0) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* file is broken */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen (void)unlink(uidlist->fname);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen uidlist->last_mtime = 0;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen } else if (ret > 0) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* success */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen uidlist->last_mtime = st.st_mtime;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen } else {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* I/O error */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (input->stream_errno == ESTALE && try_retry)
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen *retry_r = TRUE;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen else {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen errno = input->stream_errno;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen mail_storage_set_critical(storage,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen "read(%s) failed: %m", uidlist->fname);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen i_stream_destroy(&input);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen return ret;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen}
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenint maildir_uidlist_update(struct maildir_uidlist *uidlist)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen struct mail_storage *storage = &uidlist->mbox->storage->storage;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen struct stat st;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int i;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen bool retry;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen int ret;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (uidlist->last_mtime != 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (nfs_safe_stat(uidlist->fname, &st) < 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (errno != ENOENT) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "stat(%s) failed: %m", uidlist->fname);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return -1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return 0;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (st.st_mtime == uidlist->last_mtime) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* unchanged */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen return 1;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen for (i = 0; ; i++) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen ret = maildir_uidlist_update_read(uidlist, &retry,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen i < UIDLIST_ESTALE_RETRY_COUNT);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (!retry) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (ret >= 0)
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen uidlist->initial_read = TRUE;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen break;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* ESTALE - try reopening and rereading */
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return ret;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
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)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen{
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen const struct maildir_uidlist_rec *const *rec_p;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen unsigned int idx, left_idx, right_idx;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen size_t size;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (!uidlist->initial_read) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* first time we need to read uidlist */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (maildir_uidlist_update(uidlist) < 0)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return NULL;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen rec_p = buffer_get_data(uidlist->record_buf, &size);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen size /= sizeof(*rec_p);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen idx = 0;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen left_idx = 0;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen right_idx = size;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen while (left_idx < right_idx) {
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen idx = (left_idx + right_idx) / 2;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (rec_p[idx]->uid < uid)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen left_idx = idx+1;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen else if (rec_p[idx]->uid > uid)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen right_idx = idx;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen else {
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen *idx_r = idx;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen return rec_p[idx];
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen }
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen }
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen if (idx > 0) idx--;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen *idx_r = idx;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen return NULL;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen}
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainenconst char *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmaildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen enum maildir_uidlist_rec_flag *flags_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct maildir_uidlist_rec *rec;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int idx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (rec == NULL) {
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (uidlist->last_mtime != 0)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return NULL;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* the uidlist doesn't exist. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (maildir_storage_sync_force(uidlist->mbox) < 0)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* try again */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (rec == NULL)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return NULL;
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen *flags_r = rec->flags;
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen return rec->filename;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen}
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenbool maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen enum maildir_uidlist_rec_flag flags;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen if (uidlist->first_recent_uid == 0 || uid < uidlist->first_recent_uid)
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen return FALSE;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (maildir_uidlist_lookup(uidlist, uid, &flags) == NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(uidlist->first_recent_uid != uid ||
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainen (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenuint32_t maildir_uidlist_get_recent_count(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen const struct maildir_uidlist_rec *const *rec_p;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int idx;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen size_t size;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uint32_t count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!uidlist->initial_sync) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* we haven't synced yet, trust index */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen const struct mail_index_header *hdr;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen hdr = mail_index_get_header(uidlist->mbox->ibox.view);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return hdr->recent_messages_count;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
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
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (uidlist->first_recent_uid == 0)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return 0;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen rec_p = buffer_get_data(uidlist->record_buf, &size);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen size /= sizeof(*rec_p);
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen maildir_uidlist_lookup_rec(uidlist, uidlist->first_recent_uid, &idx);
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen for (count = 0; idx < size; idx++) {
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen if ((rec_p[idx]->flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen count++;
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen }
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen return count;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainenuint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen{
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return uidlist->uid_validity;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainenvoid maildir_uidlist_set_uid_validity(struct maildir_uidlist *uidlist,
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen uint32_t uid_validity, uint32_t next_uid)
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen{
ab6315aa0d5c83f4f1dc98b3715826a686aebffdTimo Sirainen uidlist->uid_validity = uid_validity;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* set next_uid only if we know newer UIDs haven't been added yet */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (uidlist->next_uid < next_uid)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->next_uid = next_uid;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenuint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return !uidlist->initial_read ? 0 : uidlist->next_uid;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstatic int maildir_uidlist_rewrite_fd(struct maildir_uidlist *uidlist,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen const char *temp_path)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct mail_storage *storage = &uidlist->mbox->storage->storage;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct maildir_uidlist_iter_ctx *iter;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen struct utimbuf ut;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen string_t *str;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen uint32_t uid;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen enum maildir_uidlist_rec_flag flags;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen const char *filename;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen int ret = 0;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen if (uidlist->delayed_rewrite) {
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen /* already written, truncate */
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen if (lseek(uidlist->lock_fd, 0, SEEK_SET) < 0) {
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen mail_storage_set_critical(storage,
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen "lseek(%s) failed: %m", temp_path);
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen return -1;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (ftruncate(uidlist->lock_fd, 0) < 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen mail_storage_set_critical(storage,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen "ftruncate(%s) failed: %m", temp_path);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen return -1;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen uidlist->version = 1;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (uidlist->uid_validity == 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* Get UIDVALIDITY from index */
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen const struct mail_index_header *hdr;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen hdr = mail_index_get_header(uidlist->mbox->ibox.view);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen uidlist->uid_validity = hdr->uid_validity;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen str = t_str_new(4096);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen str_printfa(str, "%u %u %u\n", uidlist->version,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen uidlist->uid_validity, uidlist->next_uid);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
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 if (str_len(str) + MAX_INT_STRLEN +
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen strlen(filename) + 5 + 10 >= 4096) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* flush buffer */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (write_full(uidlist->lock_fd,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen str_data(str), str_len(str)) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(storage,
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen "write_full(%s) failed: %m", temp_path);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen ret = -1;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen break;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen str_truncate(str, 0);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen str_printfa(str, "%u %s\n", uid, filename);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen }
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen maildir_uidlist_iter_deinit(iter);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (ret < 0)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen return -1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (write_full(uidlist->lock_fd, str_data(str), str_len(str)) < 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "write_full(%s) failed: %m", temp_path);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return -1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen /* uidlist's mtime must grow every time */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->last_mtime = ioloop_time <= uidlist->last_mtime ?
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->last_mtime + 1 : ioloop_time;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen ut.actime = ioloop_time;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen ut.modtime = uidlist->last_mtime;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (utime(temp_path, &ut) < 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "utime(%s) failed: %m", temp_path);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return -1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (!uidlist->mbox->ibox.fsync_disable) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (fsync(uidlist->lock_fd) < 0) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen mail_storage_set_critical(storage,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "fsync(%s) failed: %m", temp_path);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return -1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return 0;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen}
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainenstatic int maildir_uidlist_rewrite(struct maildir_uidlist *uidlist)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen struct maildir_mailbox *mbox = uidlist->mbox;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen const char *temp_path, *db_path;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen int ret;
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen i_assert(uidlist->lock_count ==
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen 1 + (uidlist->mbox->ibox.keep_locked ? 1 : 0));
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen temp_path = t_strconcat(mbox->control_dir,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "/" MAILDIR_UIDLIST_NAME ".lock", NULL);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen ret = maildir_uidlist_rewrite_fd(uidlist, temp_path);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (ret == 0 && !uidlist->mbox->ibox.keep_locked) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen db_path = t_strconcat(mbox->control_dir,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen "/" MAILDIR_UIDLIST_NAME, NULL);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
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);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen (void)unlink(temp_path);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen ret = -1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->lock_fd = -1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->lock_count--;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen } else {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if (uidlist->mbox->ibox.keep_locked)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen uidlist->delayed_rewrite = TRUE;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen maildir_uidlist_unlock(uidlist);
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstatic void maildir_uidlist_mark_all(struct maildir_uidlist *uidlist,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen bool nonsynced)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen struct maildir_uidlist_rec **rec_p;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen size_t i, size;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec_p = buffer_get_modifiable_data(uidlist->record_buf, &size);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen size /= sizeof(*rec_p);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (nonsynced) {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen for (i = 0; i < size; i++)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen rec_p[i]->flags |= MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
for (i = 0; i < size; i++)
rec_p[i]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
}
}
int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist,
enum maildir_uidlist_sync_flags sync_flags,
struct maildir_uidlist_sync_ctx **sync_ctx_r)
{
struct maildir_uidlist_sync_ctx *ctx;
size_t size;
int ret;
if ((ret = maildir_uidlist_lock(uidlist)) <= 0)
return ret;
*sync_ctx_r = ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
ctx->uidlist = uidlist;
ctx->sync_flags = sync_flags;
ctx->partial = (sync_flags & MAILDIR_UIDLIST_SYNC_PARTIAL) != 0;
if (ctx->partial) {
/* initially mark all nonsynced */
maildir_uidlist_mark_all(uidlist, TRUE);
return 1;
}
ctx->record_pool = pool_alloconly_create(MEMPOOL_GROWING
"maildir_uidlist_sync", 16384);
ctx->files = hash_create(default_pool, ctx->record_pool, 4096,
maildir_hash, maildir_cmp);
size = buffer_get_used_size(uidlist->record_buf);
ctx->record_buf = buffer_create_dynamic(default_pool, size);
return 1;
}
static int
maildir_uidlist_sync_next_partial(struct maildir_uidlist_sync_ctx *ctx,
const char *filename,
enum maildir_uidlist_rec_flag flags)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct maildir_uidlist_rec *rec;
/* we'll update uidlist directly */
rec = hash_lookup(uidlist->files, filename);
i_assert(rec != NULL || UIDLIST_IS_LOCKED(uidlist));
if (rec == NULL) {
if (ctx->new_files_count == 0) {
ctx->first_new_pos =
buffer_get_used_size(uidlist->record_buf) /
sizeof(rec);
}
ctx->new_files_count++;
if (uidlist->record_pool == NULL) {
uidlist->record_pool =
pool_alloconly_create(MEMPOOL_GROWING
"uidlist record_pool",
1024);
}
rec = p_new(uidlist->record_pool,
struct maildir_uidlist_rec, 1);
rec->uid = (uint32_t)-1;
buffer_append(uidlist->record_buf, &rec, sizeof(rec));
}
if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
rec->uid != (uint32_t)-1)
maildir_uidlist_mark_recent(uidlist, rec->uid);
rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
rec->filename = p_strdup(uidlist->record_pool, filename);
hash_insert(uidlist->files, rec->filename, rec);
return 1;
}
int maildir_uidlist_sync_next_pre(struct maildir_uidlist_sync_ctx *ctx,
const char *filename)
{
if (!UIDLIST_IS_LOCKED(ctx->uidlist) &&
hash_lookup(ctx->uidlist->files, filename) == NULL &&
(ctx->partial || hash_lookup(ctx->files, filename) == NULL)) {
if (!ctx->uidlist->initial_read) {
/* first time reading the uidlist */
if (maildir_uidlist_update(ctx->uidlist) < 0) {
ctx->failed = TRUE;
return -1;
}
return maildir_uidlist_sync_next_pre(ctx, filename);
}
return 0;
}
return 1;
}
int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
const char *filename,
enum maildir_uidlist_rec_flag flags)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct maildir_uidlist_rec *rec, *old_rec;
if (ctx->failed)
return -1;
if (ctx->partial)
return maildir_uidlist_sync_next_partial(ctx, filename, flags);
rec = hash_lookup(ctx->files, filename);
if (rec != NULL) {
if ((rec->flags & (MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
MAILDIR_UIDLIST_REC_FLAG_MOVED)) == 0) {
/* possibly duplicate */
return 0;
}
rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
MAILDIR_UIDLIST_REC_FLAG_MOVED);
} else {
old_rec = hash_lookup(uidlist->files, filename);
i_assert(old_rec != NULL || UIDLIST_IS_LOCKED(uidlist));
rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
if (old_rec != NULL)
*rec = *old_rec;
else {
rec->uid = (uint32_t)-1;
ctx->new_files_count++;
}
buffer_append(ctx->record_buf, &rec, sizeof(rec));
}
if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
rec->uid != (uint32_t)-1)
maildir_uidlist_mark_recent(uidlist, rec->uid);
rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
rec->filename = p_strdup(ctx->record_pool, filename);
hash_insert(ctx->files, rec->filename, rec);
return 1;
}
const char *
maildir_uidlist_sync_get_full_filename(struct maildir_uidlist_sync_ctx *ctx,
const char *filename)
{
struct maildir_uidlist_rec *rec;
rec = hash_lookup(ctx->files, filename);
return rec == NULL ? NULL : rec->filename;
}
const char *
maildir_uidlist_get_full_filename(struct maildir_uidlist *uidlist,
const char *filename)
{
struct maildir_uidlist_rec *rec;
rec = hash_lookup(uidlist->files, filename);
return rec == NULL ? NULL : rec->filename;
}
static int maildir_time_cmp(const void *p1, const void *p2)
{
const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
const char *s1 = (*rec1)->filename, *s2 = (*rec2)->filename;
time_t t1 = 0, t2 = 0;
/* 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) */
while (*s1 >= '0' && *s1 <= '9') {
t1 = t1*10 + (*s1 - '0');
s1++;
}
while (*s2 >= '0' && *s2 <= '9') {
t2 = t2*10 + (*s2 - '0');
s2++;
}
return t1 < t2 ? -1 : t1 > t2 ? 1 : 0;
}
static void maildir_uidlist_assign_uids(struct maildir_uidlist_sync_ctx *ctx,
unsigned int first_new_pos)
{
struct maildir_uidlist_rec **rec_p;
unsigned int dest;
size_t size;
i_assert(UIDLIST_IS_LOCKED(ctx->uidlist));
rec_p = buffer_get_modifiable_data(ctx->uidlist->record_buf, &size);
size /= sizeof(*rec_p);
/* sort new files and assign UIDs for them */
if ((ctx->sync_flags & MAILDIR_UIDLIST_SYNC_ORDERED) == 0) {
qsort(rec_p + first_new_pos, size - first_new_pos,
sizeof(*rec_p), maildir_time_cmp);
}
for (dest = first_new_pos; dest < size; dest++) {
i_assert(rec_p[dest]->uid == (uint32_t)-1);
rec_p[dest]->uid = ctx->uidlist->next_uid++;
rec_p[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
if ((rec_p[dest]->flags &
MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0) {
maildir_uidlist_mark_recent(ctx->uidlist,
rec_p[dest]->uid);
}
}
ctx->uidlist->last_seen_uid = ctx->uidlist->next_uid-1;
}
static int maildir_uid_cmp(const void *p1, const void *p2)
{
const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
return (*rec1)->uid < (*rec2)->uid ? -1 :
(*rec1)->uid > (*rec2)->uid ? 1 : 0;
}
static void maildir_uidlist_swap(struct maildir_uidlist_sync_ctx *ctx)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct maildir_uidlist_rec **rec_p;
size_t size;
/* buffer is unsorted, sort it by UID */
rec_p = buffer_get_modifiable_data(ctx->record_buf, &size);
size /= sizeof(*rec_p);
qsort(rec_p, size, sizeof(*rec_p), maildir_uid_cmp);
buffer_free(uidlist->record_buf);
uidlist->record_buf = ctx->record_buf;
ctx->record_buf = NULL;
hash_destroy(uidlist->files);
uidlist->files = ctx->files;
ctx->files = NULL;
if (uidlist->record_pool != NULL)
pool_unref(uidlist->record_pool);
uidlist->record_pool = ctx->record_pool;
ctx->record_pool = NULL;
if (ctx->new_files_count != 0)
maildir_uidlist_assign_uids(ctx, size - ctx->new_files_count);
}
void maildir_uidlist_sync_finish(struct maildir_uidlist_sync_ctx *ctx)
{
if (!ctx->partial) {
if (!ctx->failed)
maildir_uidlist_swap(ctx);
} else {
if (ctx->new_files_count != 0)
maildir_uidlist_assign_uids(ctx, ctx->first_new_pos);
}
ctx->finished = TRUE;
ctx->uidlist->initial_sync = TRUE;
}
int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx **_ctx)
{
struct maildir_uidlist_sync_ctx *ctx = *_ctx;
bool unlocked = FALSE;
int ret = ctx->failed ? -1 : 0;
*_ctx = NULL;
if (!ctx->finished)
maildir_uidlist_sync_finish(ctx);
if (ctx->partial)
maildir_uidlist_mark_all(ctx->uidlist, FALSE);
if (ctx->uidlist->need_rewrite ||
(ctx->new_files_count != 0 && !ctx->failed)) {
unsigned int nonrecursive_lock_count = 1;
if (ctx->uidlist->mbox->ibox.keep_locked)
nonrecursive_lock_count++;
if (ctx->uidlist->lock_count > nonrecursive_lock_count) {
/* recursive sync. let the root syncing do
the rewrite */
ctx->uidlist->need_rewrite = TRUE;
} else {
t_push();
ret = maildir_uidlist_rewrite(ctx->uidlist);
t_pop();
unlocked = TRUE;
if (ret == 0)
ctx->uidlist->need_rewrite = FALSE;
}
}
if (!unlocked)
maildir_uidlist_unlock(ctx->uidlist);
if (ctx->files != NULL)
hash_destroy(ctx->files);
if (ctx->record_pool != NULL)
pool_unref(ctx->record_pool);
if (ctx->record_buf != NULL)
buffer_free(ctx->record_buf);
i_free(ctx);
return ret;
}
void maildir_uidlist_add_flags(struct maildir_uidlist *uidlist,
const char *filename,
enum maildir_uidlist_rec_flag flags)
{
struct maildir_uidlist_rec *rec;
rec = hash_lookup(uidlist->files, filename);
i_assert(rec != NULL);
rec->flags |= flags;
}
struct maildir_uidlist_iter_ctx *
maildir_uidlist_iter_init(struct maildir_uidlist *uidlist)
{
struct maildir_uidlist_iter_ctx *ctx;
size_t size;
ctx = i_new(struct maildir_uidlist_iter_ctx, 1);
ctx->next = buffer_get_data(uidlist->record_buf, &size);
size /= sizeof(*ctx->next);
ctx->end = ctx->next + size;
return ctx;
}
int maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx,
uint32_t *uid_r,
enum maildir_uidlist_rec_flag *flags_r,
const char **filename_r)
{
if (ctx->next == ctx->end)
return 0;
*uid_r = (*ctx->next)->uid;
*flags_r = (*ctx->next)->flags;
*filename_r = (*ctx->next)->filename;
ctx->next++;
return 1;
}
void maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx *ctx)
{
i_free(ctx);
}