From: Timo Sirainen Date: Thu, 3 Jun 2004 15:01:27 +0000 (+0300) Subject: Separate mbox_locks/mbox_read_dotlock to mbox_read_locks and X-Git-Tag: 1.1.alpha1~4003 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc1696e32dd732a5bbabc3c8f64810448e327043;p=thirdparty%2Fdovecot%2Fcore.git Separate mbox_locks/mbox_read_dotlock to mbox_read_locks and mbox_write_locks. Added support for lockf() method. --HG-- branch : HEAD --- diff --git a/configure.in b/configure.in index 0c32ec4a8e..27b6fd712d 100644 --- a/configure.in +++ b/configure.in @@ -251,7 +251,7 @@ if test "$have_fdatasync" = "yes"; then fi dnl * after -lsocket and -lnsl tests, inet_aton() may be in them -AC_CHECK_FUNCS(fcntl flock inet_aton sigaction getpagesize madvise \ +AC_CHECK_FUNCS(fcntl flock lockf inet_aton sigaction getpagesize madvise \ strcasecmp stricmp vsnprintf vsyslog writev pread \ setrlimit setproctitle) diff --git a/dovecot-example.conf b/dovecot-example.conf index 466b263c55..fd2b03c721 100644 --- a/dovecot-example.conf +++ b/dovecot-example.conf @@ -283,24 +283,21 @@ # specifies that existing messages are immutable. #maildir_check_content_changes = no -# Which locking methods to use for locking mbox. There's three available: +# Which locking methods to use for locking mbox. There's four available: # dotlock: Create .lock file. This is the oldest and most NFS-safe # solution. If you want to use /var/mail/ like directory, the users # will need write access to that directory. # fcntl : Use this if possible. Works with NFS too if lockd is used. # flock : May not exist in all systems. Doesn't work with NFS. +# lockf : May not exist in all systems. Doesn't work with NFS. # -# You can use both fcntl and flock too; if you do the order they're declared -# with is important to avoid deadlocks if other MTAs/MUAs are using both fcntl -# and flock. Some operating systems don't allow using both of them -# simultaneously, eg. BSDs. If dotlock is used, it's always created first. -#mbox_locks = dotlock fcntl - -# Should we create dotlock file even when we want only a read-lock? Setting -# this to yes hurts the performance when the mailbox is accessed simultaneously -# by multiple processes, but it's needed for reliable reading if no other -# locking methods are available. -#mbox_read_dotlock = no +# You can use multiple locking methods; if you do the order they're declared +# in is important to avoid deadlocks if other MTAs/MUAs are using multiple +# locking methods as well. Some operating systems don't allow using some of +# them simultaneously. If dotlocking is used, it must always be first in the +# list. +#mbox_read_locks = fcntl +#mbox_write_locks = dotlock fcntl # Maximum time in seconds to wait for lock (all of them) before aborting. #mbox_lock_timeout = 300 diff --git a/src/lib-storage/index/mbox/mbox-lock.c b/src/lib-storage/index/mbox/mbox-lock.c index 276e028491..618277f822 100644 --- a/src/lib-storage/index/mbox/mbox-lock.c +++ b/src/lib-storage/index/mbox/mbox-lock.c @@ -19,7 +19,8 @@ #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000) /* lock methods to use in wanted order */ -#define DEFAULT_LOCK_METHODS "dotlock fcntl" +#define DEFAULT_READ_LOCK_METHODS "fcntl" +#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl" /* lock timeout */ #define DEFAULT_LOCK_TIMEOUT 300 /* assume stale dotlock if mbox file hasn't changed for n seconds */ @@ -31,34 +32,98 @@ struct dotlock_context { int last_stale; }; +enum mbox_lock_type { + MBOX_LOCK_DOTLOCK, + MBOX_LOCK_FCNTL, + MBOX_LOCK_FLOCK, + MBOX_LOCK_LOCKF, + + MBOX_LOCK_COUNT +}; + +struct mbox_lock_data { + enum mbox_lock_type type; + const char *name; + int (*func)(struct index_mailbox *ibox, int lock_type, + time_t max_wait_time); +}; + +static int mbox_lock_fcntl(struct index_mailbox *ibox, int lock_type, + time_t max_wait_time); +#ifdef HAVE_FLOCK +static int mbox_lock_flock(struct index_mailbox *ibox, int lock_type, + time_t max_wait_time); +#else +# define mbox_lock_flock NULL +#endif +#ifdef HAVE_LOCKF +static int mbox_lock_lockf(struct index_mailbox *ibox, int lock_type, + time_t max_wait_time); +#else +# define mbox_lock_lockf NULL +#endif + +struct mbox_lock_data lock_data[] = { + { MBOX_LOCK_DOTLOCK, "dotlock", NULL }, + { MBOX_LOCK_FCNTL, "fcntl", mbox_lock_fcntl }, + { MBOX_LOCK_FLOCK, "flock", mbox_lock_flock }, + { MBOX_LOCK_LOCKF, "lockf", mbox_lock_lockf }, + { 0, NULL, NULL } +}; + static int lock_settings_initialized = FALSE; -static int use_dotlock, use_fcntl_lock, use_flock, fcntl_before_flock; -static int use_read_dotlock, lock_timeout, dotlock_change_timeout; +static enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1]; +static enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1]; +static int lock_timeout, dotlock_change_timeout; static int mbox_unlock_files(struct index_mailbox *ibox); -static void mbox_init_lock_settings(void) +static void mbox_read_lock_methods(const char *str, const char *env, + enum mbox_lock_type *locks) { - const char *str; + enum mbox_lock_type type; const char *const *lock; + int i, dest; + + for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) { + for (type = 0; lock_data[type].name != NULL; type++) { + if (strcasecmp(*lock, lock_data[type].name) == 0) { + type = lock_data[type].type; + break; + } + } + if (lock_data[type].name == NULL) + i_fatal("%s: Invalid value %s", env, *lock); + if (lock_data[type].func == NULL && type != MBOX_LOCK_DOTLOCK) { + i_fatal("%s: Support for lock type %s " + "not compiled into binary", env, *lock); + } - use_dotlock = use_fcntl_lock = use_flock = fcntl_before_flock = FALSE; - - str = getenv("MBOX_LOCKS"); - if (str == NULL) str = DEFAULT_LOCK_METHODS; - for (lock = t_strsplit(str, " "); *lock != NULL; lock++) { - if (strcasecmp(*lock, "dotlock") == 0) - use_dotlock = TRUE; - else if (strcasecmp(*lock, "fcntl") == 0) { - use_fcntl_lock = TRUE; - fcntl_before_flock = use_flock == FALSE; - } else if (strcasecmp(*lock, "flock") == 0) - use_flock = TRUE; - else - i_fatal("MBOX_LOCKS: Invalid value %s", *lock); + for (i = 0; i < dest; i++) { + if (locks[i] == type) + i_fatal("%s: Duplicated value %s", env, *lock); + } + + if (type == MBOX_LOCK_DOTLOCK && dest != 0) + i_fatal("%s: dotlock must be first in the list", *lock); + + /* @UNSAFE */ + locks[dest++] = type; } + locks[dest] = (enum mbox_lock_type)-1; +} + +static void mbox_init_lock_settings(void) +{ + const char *str; + + str = getenv("MBOX_READ_LOCKS"); + if (str == NULL) str = DEFAULT_READ_LOCK_METHODS; + mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks); - use_read_dotlock = getenv("MBOX_READ_DOTLOCK") != NULL; + str = getenv("MBOX_WRITE_LOCKS"); + if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS; + mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks); str = getenv("MBOX_LOCK_TIMEOUT"); lock_timeout = str == NULL ? DEFAULT_LOCK_TIMEOUT : atoi(str); @@ -110,6 +175,44 @@ static int mbox_lock_flock(struct index_mailbox *ibox, int lock_type, } #endif +#ifdef HAVE_LOCKF +static int mbox_lock_lockf(struct index_mailbox *ibox, int lock_type, + time_t max_wait_time) +{ + time_t now, last_notify; + + if (lock_type != F_UNLCK) + lock_type = F_TLOCK; + else + lock_type = F_ULOCK; + + last_notify = 0; + while (lockf(ibox->mbox_fd, lock_type, 0) < 0) { + if (errno != EAGAIN) { + mbox_set_syscall_error(ibox, "lockf()"); + return -1; + } + + if (max_wait_time == 0) + return 0; + + now = time(NULL); + if (now >= max_wait_time) + return 0; + + if (now != last_notify) { + index_storage_lock_notify(ibox, + MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, + max_wait_time - now); + } + + usleep(LOCK_RANDOM_USLEEP_TIME); + } + + return 1; +} +#endif + static int mbox_lock_fcntl(struct index_mailbox *ibox, int lock_type, time_t max_wait_time) { @@ -145,8 +248,9 @@ static int mbox_lock_fcntl(struct index_mailbox *ibox, int lock_type, static int mbox_file_locks(struct index_mailbox *ibox, int lock_type, time_t max_wait_time) { + enum mbox_lock_type *lock_types; struct stat st; - int ret; + int i, ret; /* now we need to have the file itself locked. open it if needed. */ if (stat(ibox->path, &st) < 0) { @@ -164,36 +268,30 @@ static int mbox_file_locks(struct index_mailbox *ibox, int lock_type, } } - if (use_fcntl_lock && fcntl_before_flock) { - ret = mbox_lock_fcntl(ibox, lock_type, max_wait_time); - if (ret <= 0) - return ret; - } -#ifdef HAVE_FLOCK - if (use_flock) { - ret = mbox_lock_flock(ibox, lock_type, max_wait_time); - if (ret <= 0) - return ret; - } -#endif - if (use_fcntl_lock && !fcntl_before_flock) { - ret = mbox_lock_fcntl(ibox, lock_type, max_wait_time); - if (ret <= 0) - return ret; + lock_types = lock_type == F_WRLCK ? write_locks : read_locks; + for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) { + if (lock_data[lock_types[i]].type != MBOX_LOCK_DOTLOCK) { + ret = lock_data[lock_types[i]].func(ibox, lock_type, + max_wait_time); + if (ret <= 0) + return ret; + } } return 1; } static int mbox_file_unlock(struct index_mailbox *ibox) { - int ret = 0; - -#ifdef HAVE_FLOCK - if (use_flock && mbox_lock_flock(ibox, F_UNLCK, 0) < 0) - ret = -1; -#endif - if (use_fcntl_lock && mbox_lock_fcntl(ibox, F_UNLCK, 0) < 0) - ret = -1; + enum mbox_lock_type *lock_types; + int i, ret = 0; + + lock_types = ibox->mbox_lock_type == F_WRLCK ? write_locks : read_locks; + for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) { + if (lock_data[lock_types[i]].type != MBOX_LOCK_DOTLOCK) { + if (lock_data[lock_types[i]].func(ibox, F_UNLCK, 0) < 0) + ret = -1; + } + } return ret; } @@ -242,16 +340,16 @@ int mbox_lock(struct index_mailbox *ibox, int lock_type, max_wait_time = time(NULL) + lock_timeout; /* make .lock file first to protect overwriting the file */ - if (use_dotlock && ibox->mbox_dotlock.ino == 0) { + if (((lock_type == F_RDLCK && read_locks[0] == MBOX_LOCK_DOTLOCK) || + (lock_type == F_WRLCK && write_locks[0] == MBOX_LOCK_DOTLOCK)) && + ibox->mbox_dotlock.ino == 0) { struct dotlock_context ctx; ctx.ibox = ibox; ctx.lock_type = lock_type; ctx.last_stale = -1; - ret = file_lock_dotlock(ibox->path, NULL, - lock_type == F_RDLCK && - !use_read_dotlock, lock_timeout, + ret = file_lock_dotlock(ibox->path, NULL, FALSE, lock_timeout, dotlock_change_timeout, 0, dotlock_callback, &ctx, &ibox->mbox_dotlock); diff --git a/src/lib-storage/index/mbox/mbox-sync-parse.c b/src/lib-storage/index/mbox/mbox-sync-parse.c index f5bbcdb549..40d94e161d 100644 --- a/src/lib-storage/index/mbox/mbox-sync-parse.c +++ b/src/lib-storage/index/mbox/mbox-sync-parse.c @@ -108,6 +108,11 @@ static int parse_x_imap_base(struct mbox_sync_mail_context *ctx, ctx->sync_ctx->next_uid = uid_last+1; } + if (ctx->sync_ctx->prev_msg_uid >= ctx->sync_ctx->next_uid) { + /* broken, update */ + ctx->sync_ctx->next_uid = ctx->sync_ctx->prev_msg_uid+1; + } + ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] = str_len(ctx->header); ctx->seen_imapbase = TRUE; @@ -167,6 +172,11 @@ static int parse_x_uid(struct mbox_sync_mail_context *ctx, extra_space++; } + if (value >= ctx->sync_ctx->next_uid) { + /* next_uid broken - fix it */ + ctx->sync_ctx->next_uid = value+1; + } + if (value <= ctx->sync_ctx->prev_msg_uid) { /* broken - UIDs must be growing */ return FALSE; diff --git a/src/lib-storage/index/mbox/mbox-sync.c b/src/lib-storage/index/mbox/mbox-sync.c index 1f0ecfe125..d6749b027c 100644 --- a/src/lib-storage/index/mbox/mbox-sync.c +++ b/src/lib-storage/index/mbox/mbox-sync.c @@ -166,7 +166,7 @@ static int mbox_sync_do(struct index_mailbox *ibox, buffer_t *mails, *syncs; size_t size; struct stat st; - int ret = 0; + int sync_expunge, ret = 0; t = mail_index_transaction_begin(sync_view, FALSE); @@ -205,13 +205,19 @@ static int mbox_sync_do(struct index_mailbox *ibox, mbox_sync_next_mail(&sync_ctx, &mail_ctx, seq); /* get all sync records related to this message */ - ret = 1; + ret = 1; sync_expunge = FALSE; mbox_sync_buffer_delete_old(syncs, mail_ctx.mail.uid); while (mail_ctx.mail.uid >= sync_rec.uid1 && ret > 0) { if (sync_rec.uid1 != 0) { i_assert(mail_ctx.mail.uid <= sync_rec.uid2); buffer_append(syncs, &sync_rec, sizeof(sync_rec)); + + if (sync_rec.type == + MAIL_INDEX_SYNC_TYPE_EXPUNGE) { + sync_expunge = TRUE; + break; + } } ret = mail_index_sync_next(index_sync_ctx, &sync_rec); if (ret == 0) @@ -278,7 +284,9 @@ static int mbox_sync_do(struct index_mailbox *ibox, break; } - if (rec != NULL) { + if (sync_expunge) { + /* .. */ + } else if (rec != NULL) { /* see if flags changed */ keywords_mask_t old_keywords; uint8_t old_flags; @@ -340,7 +348,7 @@ static int mbox_sync_do(struct index_mailbox *ibox, } } - if (need_space_seq != 0) { + if (need_space_seq != 0 && ret >= 0) { i_assert(space_diff < 0); extra_space = MBOX_HEADER_EXTRA_SPACE * (seq - need_space_seq + 1); @@ -362,13 +370,16 @@ static int mbox_sync_do(struct index_mailbox *ibox, } } - if (rec != NULL) - mail_index_expunge(t, idx_seq); - while (idx_seq < messages_count) - mail_index_expunge(t, ++idx_seq); + if (ret >= 0) { + if (rec != NULL) + mail_index_expunge(t, idx_seq); + while (idx_seq < messages_count) + mail_index_expunge(t, ++idx_seq); + + if (sync_ctx.base_uid_last+1 != sync_ctx.next_uid) { - if (sync_ctx.base_uid_last+1 != sync_ctx.next_uid) { - // FIXME: rewrite X-IMAPbase header + // FIXME: rewrite X-IMAPbase header + } } if (ret >= 0) {