mbox-lock.c revision 43d32cbe60fdaef2699d99f1ca259053e9350411
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* Copyright (C) 2002-2007 Timo Sirainen */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* 0.1 .. 0.2msec */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* lock methods to use in wanted order */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* lock timeout */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen/* assume stale dotlock if mbox file hasn't changed for n seconds */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int (*func)(struct mbox_lock_context *ctx, int lock_type,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainenstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen { MBOX_LOCK_DOTLOCK, "dotlock", mbox_lock_dotlock },
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen { MBOX_LOCK_DOTLOCK_TRY, "dotlock_try", mbox_lock_dotlock_try },
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen { MBOX_LOCK_FCNTL, "fcntl", mbox_lock_fcntl },
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen { MBOX_LOCK_FLOCK, "flock", mbox_lock_flock },
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen { MBOX_LOCK_LOCKF, "lockf", mbox_lock_lockf },
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1];
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int lock_timeout, dotlock_change_timeout;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_unlock_files(struct mbox_lock_context *ctx);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void mbox_read_lock_methods(const char *str, const char *env,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *const *lock;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (type = 0; lock_data[type].name != NULL; type++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (strcasecmp(*lock, lock_data[type].name) == 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < dest; i++) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_fatal("%s: Duplicated value %s", env, *lock);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* @UNSAFE */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *str;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (str == NULL) str = DEFAULT_READ_LOCK_METHODS;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks);
6d2b3ce2c6ef62334985ece4f0ab8b154e0e9560Timo Sirainen if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS;
6d2b3ce2c6ef62334985ece4f0ab8b154e0e9560Timo Sirainen mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks);
6d2b3ce2c6ef62334985ece4f0ab8b154e0e9560Timo Sirainen /* check that read/write list orders match. write_locks must contain
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen at least read_locks and possibly more. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (read_locks[r] != (enum mbox_lock_type)-1) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_fatal("mbox read/write lock list settings are invalid. "
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen "Lock ordering must be the same with both, "
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen "and write locks must contain all read locks "
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen "(and possibly more)");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen lock_timeout = str == NULL ? MBOX_DEFAULT_LOCK_TIMEOUT : atoi(str);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ctx->checked_file || lock_type == F_UNLCK)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* get next index we wish to try locking. it's the one after
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen dotlocking. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (lock_types[i] != (enum mbox_lock_type)-1 &&
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (mbox_lock_list(ctx, ctx->lock_type, 0, i) <= 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* we couldn't get fd lock -
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen it's really locked */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen index_storage_lock_notify(&ctx->mbox->ibox, stale ?
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenmbox_lock_dotlock_int(struct mbox_lock_context *ctx, int lock_type, bool try)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (file_dotlock_delete(&mbox->mbox_dotlock) <= 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mbox_set_syscall_error(mbox, "file_dotlock_delete()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen set.use_excl_lock = (mbox->storage->storage.flags &
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ret = file_dotlock_create(&set, mbox->path, 0, &mbox->mbox_dotlock);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if ((ENOSPACE(errno) || errno == EACCES) && try)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox_set_syscall_error(mbox, "file_lock_dotlock()");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_storage_set_error(&mbox->storage->storage,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return mbox_lock_dotlock_int(ctx, lock_type, FALSE);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return mbox_lock_dotlock_int(ctx, lock_type, TRUE);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen while (flock(ctx->mbox->mbox_fd, lock_type | LOCK_NB) < 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen while (lockf(ctx->mbox->mbox_fd, lock_type, 0) < 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (mbox_file_open_latest(ctx, lock_type) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* usually we're waiting here, but if we came from
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mbox_lock_dotlock(), we just want to try locking */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen while (fcntl(ctx->mbox->mbox_fd, wait_type, &fl) < 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* non-blocking lock trying failed */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* notify locks once every 5 seconds.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen try to use rounded values. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ?
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ret = lock_data[type].func(ctx, lock_type, max_wait_time);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mbox_update_locking(struct mbox_mailbox *mbox, int lock_type,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* read-only mbox stream. no need to lock. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* dropping to shared lock. first drop those that we
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen don't remove completely. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (i = 0; i < MBOX_LOCK_COUNT; i++)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ret = mbox_lock_list(&ctx, lock_type, max_wait_time, 0);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_storage_set_error(&mbox->storage->storage,
unsigned int *lock_id_r)
bool fcntl_locked;
int ret;
if (ret <= 0)
return ret;
MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) {
int ret = 0;
return ret;
bool fcntl_locked;
&fcntl_locked) < 0)
for (i = 0; i < MBOX_LOCK_COUNT; i++)