]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Separate mbox_locks/mbox_read_dotlock to mbox_read_locks and
authorTimo Sirainen <tss@iki.fi>
Thu, 3 Jun 2004 15:01:27 +0000 (18:01 +0300)
committerTimo Sirainen <tss@iki.fi>
Thu, 3 Jun 2004 15:01:27 +0000 (18:01 +0300)
mbox_write_locks. Added support for lockf() method.

--HG--
branch : HEAD

configure.in
dovecot-example.conf
src/lib-storage/index/mbox/mbox-lock.c
src/lib-storage/index/mbox/mbox-sync-parse.c
src/lib-storage/index/mbox/mbox-sync.c

index 0c32ec4a8eee4208b3fc5bf7daeb87e1a0229f86..27b6fd712d6c1fe9d57423da89837c0422c5a932 100644 (file)
@@ -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)
 
index 466b263c559485b744eb8d907972aa9ebae9fe5d..fd2b03c721c4b879010c2492f4d63b0cb264104a 100644 (file)
 # 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 <mailbox>.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
index 276e028491763b9d4c3754787a6de72b0897c8d4..618277f82200de26700c1fa692ab811fd5a61be7 100644 (file)
@@ -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);
index f5bbcdb5496018c528edb8b4af9a403315976baf..40d94e161d240f2a3fa7a05b1c0fd19be09887cd 100644 (file)
@@ -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;
index 1f0ecfe1258dd1bdebbd182ccb5290a40a0c7604..d6749b027c3a870459eda60e0a13e2893eaf7439 100644 (file)
@@ -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) {