mbox-lock.c revision 181c1aff950e6f8e0556f8974e79d0747845ac0f
1516N/A/* Copyright (C) 2002-2007 Timo Sirainen */
39N/A
39N/A#include "lib.h"
39N/A#include "nfs-workarounds.h"
39N/A#include "mail-index-private.h"
39N/A#include "mbox-storage.h"
39N/A#include "mbox-file.h"
39N/A#include "mbox-lock.h"
39N/A
39N/A#include <time.h>
39N/A#include <stdlib.h>
39N/A#include <unistd.h>
39N/A#include <fcntl.h>
39N/A#include <sys/stat.h>
39N/A
39N/A#ifdef HAVE_FLOCK
39N/A# include <sys/file.h>
39N/A#endif
39N/A
39N/A/* 0.1 .. 0.2msec */
39N/A#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
926N/A
926N/A/* lock methods to use in wanted order */
1890N/A#define DEFAULT_READ_LOCK_METHODS "fcntl"
926N/A#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl"
39N/A/* lock timeout */
1713N/A#define MBOX_DEFAULT_LOCK_TIMEOUT (5*60)
2026N/A/* assume stale dotlock if mbox file hasn't changed for n seconds */
342N/A#define DEFAULT_DOTLOCK_CHANGE_TIMEOUT (120)
1516N/A
1636N/Aenum mbox_lock_type {
1386N/A MBOX_LOCK_DOTLOCK,
838N/A MBOX_LOCK_FCNTL,
39N/A MBOX_LOCK_FLOCK,
51N/A MBOX_LOCK_LOCKF,
2073N/A
1066N/A MBOX_LOCK_COUNT
1231N/A};
1352N/A
1890N/Astruct mbox_lock_context {
296N/A struct mbox_mailbox *mbox;
39N/A int lock_status[MBOX_LOCK_COUNT];
1713N/A bool checked_file;
1713N/A
39N/A int lock_type;
39N/A bool dotlock_last_stale;
39N/A bool fcntl_locked;
39N/A};
39N/A
205N/Astruct mbox_lock_data {
39N/A enum mbox_lock_type type;
205N/A const char *name;
39N/A int (*func)(struct mbox_lock_context *ctx, int lock_type,
39N/A time_t max_wait_time);
39N/A};
39N/A
39N/Astatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
39N/A time_t max_wait_time);
39N/Astatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
39N/A time_t max_wait_time);
39N/A#ifdef HAVE_FLOCK
39N/Astatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
39N/A time_t max_wait_time);
39N/A#else
39N/A# define mbox_lock_flock NULL
39N/A#endif
39N/A#ifdef HAVE_LOCKF
39N/Astatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
39N/A time_t max_wait_time);
39N/A#else
39N/A# define mbox_lock_lockf NULL
39N/A#endif
39N/A
39N/Astruct mbox_lock_data lock_data[] = {
48N/A { MBOX_LOCK_DOTLOCK, "dotlock", mbox_lock_dotlock },
48N/A { MBOX_LOCK_FCNTL, "fcntl", mbox_lock_fcntl },
48N/A { MBOX_LOCK_FLOCK, "flock", mbox_lock_flock },
48N/A { MBOX_LOCK_LOCKF, "lockf", mbox_lock_lockf },
48N/A { 0, NULL, NULL }
59N/A};
48N/A
2073N/Astatic bool lock_settings_initialized = FALSE;
2073N/Astatic enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1];
39N/Astatic enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
39N/Astatic int lock_timeout, dotlock_change_timeout;
567N/A
838N/Astatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
838N/A time_t max_wait_time, int idx);
838N/Astatic int mbox_unlock_files(struct mbox_lock_context *ctx);
1890N/A
39N/Astatic void mbox_read_lock_methods(const char *str, const char *env,
39N/A enum mbox_lock_type *locks)
39N/A{
1431N/A enum mbox_lock_type type;
1431N/A const char *const *lock;
39N/A int i, dest;
227N/A
315N/A for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) {
315N/A for (type = 0; lock_data[type].name != NULL; type++) {
39N/A if (strcasecmp(*lock, lock_data[type].name) == 0) {
1352N/A type = lock_data[type].type;
1352N/A break;
1352N/A }
1352N/A }
1431N/A if (lock_data[type].name == NULL)
1431N/A i_fatal("%s: Invalid value %s", env, *lock);
429N/A if (lock_data[type].func == NULL) {
315N/A i_fatal("%s: Support for lock type %s "
1352N/A "not compiled into binary", env, *lock);
429N/A }
1352N/A
1352N/A for (i = 0; i < dest; i++) {
39N/A if (locks[i] == type)
926N/A i_fatal("%s: Duplicated value %s", env, *lock);
926N/A }
203N/A
203N/A /* @UNSAFE */
203N/A locks[dest++] = type;
203N/A }
1045N/A locks[dest] = (enum mbox_lock_type)-1;
1045N/A}
72N/A
72N/Astatic void mbox_init_lock_settings(void)
59N/A{
2054N/A const char *str;
1045N/A int r, w;
1045N/A
1045N/A str = getenv("MBOX_READ_LOCKS");
1045N/A if (str == NULL) str = DEFAULT_READ_LOCK_METHODS;
1713N/A mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks);
1045N/A
1045N/A str = getenv("MBOX_WRITE_LOCKS");
1045N/A if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS;
2084N/A mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks);
2084N/A
2084N/A /* check that read/write list orders match. write_locks must contain
2084N/A at least read_locks and possibly more. */
2084N/A for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) {
2084N/A if (read_locks[r] == (enum mbox_lock_type)-1)
2084N/A break;
72N/A if (read_locks[r] == write_locks[w])
2084N/A r++;
838N/A }
72N/A if (read_locks[r] != (enum mbox_lock_type)-1) {
72N/A i_fatal("mbox read/write lock list settings are invalid. "
2084N/A "Lock ordering must be the same with both, "
838N/A "and write locks must contain all read locks "
72N/A "(and possibly more)");
59N/A }
72N/A
72N/A str = getenv("MBOX_LOCK_TIMEOUT");
59N/A lock_timeout = str == NULL ? MBOX_DEFAULT_LOCK_TIMEOUT : atoi(str);
72N/A
72N/A str = getenv("MBOX_DOTLOCK_CHANGE_TIMEOUT");
307N/A dotlock_change_timeout = str == NULL ?
307N/A DEFAULT_DOTLOCK_CHANGE_TIMEOUT : atoi(str);
307N/A
72N/A lock_settings_initialized = TRUE;
72N/A}
72N/A
237N/Astatic int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type)
72N/A{
59N/A struct mbox_mailbox *mbox = ctx->mbox;
72N/A struct stat st;
72N/A
72N/A if (ctx->checked_file || lock_type == F_UNLCK)
72N/A return 0;
59N/A
72N/A if (mbox->mbox_fd != -1) {
72N/A if ((mbox->storage->storage.flags &
72N/A MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0)
202N/A nfs_flush_attr_cache(mbox->path);
72N/A if (nfs_safe_stat(mbox->path, &st) < 0) {
72N/A mbox_set_syscall_error(mbox, "stat()");
59N/A return -1;
1713N/A }
59N/A
838N/A if (st.st_ino != mbox->mbox_ino ||
838N/A !CMP_DEV_T(st.st_dev, mbox->mbox_dev))
838N/A mbox_file_close(mbox);
838N/A }
838N/A
926N/A if (mbox->mbox_fd == -1) {
838N/A if (mbox_file_open(mbox) < 0)
838N/A return -1;
838N/A }
838N/A
926N/A ctx->checked_file = TRUE;
838N/A return 0;
926N/A}
926N/A
926N/Astatic bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
838N/A{
838N/A struct mbox_lock_context *ctx = context;
838N/A enum mbox_lock_type *lock_types;
838N/A int i;
838N/A
838N/A if (stale && !ctx->dotlock_last_stale) {
838N/A /* get next index we wish to try locking. it's the one after
838N/A dotlocking. */
838N/A lock_types = ctx->lock_type == F_WRLCK ||
926N/A (ctx->lock_type == F_UNLCK &&
838N/A ctx->mbox->mbox_lock_type == F_WRLCK) ?
838N/A write_locks : read_locks;
838N/A
838N/A for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) {
838N/A if (lock_types[i] == MBOX_LOCK_DOTLOCK)
838N/A break;
838N/A }
845N/A
845N/A if (lock_types[i] != (enum mbox_lock_type)-1 &&
926N/A lock_types[i+1] != (enum mbox_lock_type)-1) {
838N/A i++;
845N/A if (mbox_lock_list(ctx, ctx->lock_type, 0, i) <= 0) {
845N/A /* we couldn't get fd lock -
845N/A it's really locked */
838N/A ctx->dotlock_last_stale = TRUE;
845N/A return FALSE;
845N/A }
838N/A (void)mbox_lock_list(ctx, F_UNLCK, 0, i);
838N/A }
926N/A }
203N/A ctx->dotlock_last_stale = stale;
315N/A
838N/A index_storage_lock_notify(&ctx->mbox->ibox, stale ?
203N/A MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE :
838N/A MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
48N/A secs_left);
48N/A return TRUE;
48N/A}
48N/A
48N/Astatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
838N/A time_t max_wait_time __attr_unused__)
203N/A{
48N/A struct mbox_mailbox *mbox = ctx->mbox;
203N/A struct dotlock_settings set;
72N/A int ret;
181N/A
72N/A if (lock_type == F_UNLCK) {
181N/A if (!mbox->mbox_dotlocked)
46N/A return 1;
181N/A
237N/A if (file_dotlock_delete(&mbox->mbox_dotlock) <= 0) {
46N/A mbox_set_syscall_error(mbox, "file_dotlock_delete()");
838N/A ret = -1;
838N/A }
838N/A mbox->mbox_dotlocked = FALSE;
838N/A return 1;
838N/A }
838N/A
838N/A if (mbox->mbox_dotlocked)
838N/A return 1;
342N/A
926N/A ctx->dotlock_last_stale = -1;
838N/A
838N/A memset(&set, 0, sizeof(set));
926N/A set.use_excl_lock = (mbox->storage->storage.flags &
838N/A MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0;
838N/A set.timeout = lock_timeout;
838N/A set.stale_timeout = dotlock_change_timeout;
838N/A set.callback = dotlock_callback;
838N/A set.context = ctx;
926N/A
926N/A ret = file_dotlock_create(&set, mbox->path, 0, &mbox->mbox_dotlock);
615N/A if (ret < 0) {
615N/A mbox_set_syscall_error(mbox, "file_lock_dotlock()");
615N/A return -1;
615N/A }
615N/A if (ret == 0) {
926N/A mail_storage_set_error(&mbox->storage->storage,
615N/A MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT);
615N/A return 0;
838N/A }
111N/A mbox->mbox_dotlocked = TRUE;
111N/A
111N/A if (mbox_file_open_latest(ctx, lock_type) < 0)
111N/A return -1;
111N/A return 1;
111N/A}
111N/A
111N/A#ifdef HAVE_FLOCK
113N/Astatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
926N/A time_t max_wait_time)
838N/A{
926N/A time_t now, last_notify;
113N/A
113N/A if (mbox_file_open_latest(ctx, lock_type) < 0)
113N/A return -1;
113N/A
113N/A if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
113N/A return 1;
113N/A
113N/A if (lock_type == F_WRLCK)
113N/A lock_type = LOCK_EX;
111N/A else if (lock_type == F_RDLCK)
1500N/A lock_type = LOCK_SH;
1432N/A else
1542N/A lock_type = LOCK_UN;
1970N/A
2073N/A last_notify = 0;
2073N/A while (flock(ctx->mbox->mbox_fd, lock_type | LOCK_NB) < 0) {
2073N/A if (errno != EWOULDBLOCK) {
2073N/A mbox_set_syscall_error(ctx->mbox, "flock()");
2073N/A return -1;
2073N/A }
1542N/A
146N/A now = time(NULL);
1432N/A if (now >= max_wait_time)
1432N/A return 0;
50N/A
1432N/A if (now != last_notify) {
1432N/A index_storage_lock_notify(&ctx->mbox->ibox,
1432N/A MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
51N/A max_wait_time - now);
1636N/A }
1432N/A
1507N/A usleep(LOCK_RANDOM_USLEEP_TIME);
51N/A }
1500N/A
591N/A return 1;
1970N/A}
1970N/A#endif
1970N/A
591N/A#ifdef HAVE_LOCKF
1542N/Astatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
1970N/A time_t max_wait_time)
1970N/A{
1970N/A time_t now, last_notify;
2073N/A
1500N/A if (mbox_file_open_latest(ctx, lock_type) < 0)
2073N/A return -1;
2073N/A
1713N/A if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
1713N/A return 1;
2073N/A
2073N/A if (lock_type != F_UNLCK)
2073N/A lock_type = F_TLOCK;
1713N/A else
2073N/A lock_type = F_ULOCK;
1713N/A
2073N/A last_notify = 0;
2073N/A while (lockf(ctx->mbox->mbox_fd, lock_type, 0) < 0) {
2073N/A if (errno != EAGAIN) {
1713N/A mbox_set_syscall_error(ctx->mbox, "lockf()");
2073N/A return -1;
2073N/A }
2073N/A
1713N/A now = time(NULL);
1713N/A if (now >= max_wait_time)
2073N/A return 0;
2073N/A
2073N/A if (now != last_notify) {
1500N/A index_storage_lock_notify(&ctx->mbox->ibox,
1500N/A MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
1500N/A max_wait_time - now);
1500N/A }
1500N/A
51N/A usleep(LOCK_RANDOM_USLEEP_TIME);
1500N/A }
1500N/A
1500N/A return 1;
1500N/A}
1500N/A#endif
1500N/A
1500N/Astatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
2073N/A time_t max_wait_time)
2073N/A{
2073N/A struct flock fl;
2073N/A time_t now;
2073N/A unsigned int next_alarm;
2073N/A int wait_type;
1890N/A
1500N/A if (mbox_file_open_latest(ctx, lock_type) < 0)
2073N/A return -1;
2073N/A
2073N/A if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
1500N/A return 1;
1500N/A
1500N/A memset(&fl, 0, sizeof(fl));
1500N/A fl.l_type = lock_type;
926N/A fl.l_whence = SEEK_SET;
1500N/A fl.l_start = 0;
2026N/A fl.l_len = 0;
1500N/A
2026N/A if (max_wait_time == 0) {
2026N/A /* usually we're waiting here, but if we came from
2026N/A mbox_lock_dotlock(), we just want to try locking */
2026N/A wait_type = F_SETLK;
2073N/A } else {
2026N/A wait_type = F_SETLKW;
2026N/A now = time(NULL);
1500N/A if (now >= max_wait_time)
1500N/A alarm(1);
1500N/A else
1500N/A alarm(I_MIN(max_wait_time - now, 5));
1500N/A }
123N/A
1500N/A while (fcntl(ctx->mbox->mbox_fd, wait_type, &fl) < 0) {
1500N/A if (errno != EINTR) {
877N/A if ((errno == EACCES || errno == EAGAIN) &&
1500N/A wait_type == F_SETLK) {
1500N/A /* non-blocking lock trying failed */
1500N/A return 0;
1500N/A }
1500N/A mbox_set_syscall_error(ctx->mbox, "fcntl()");
1500N/A alarm(0);
1500N/A return -1;
1500N/A }
1500N/A
1500N/A now = time(NULL);
1500N/A if (now >= max_wait_time) {
1500N/A alarm(0);
1500N/A return 0;
1500N/A }
39N/A
1500N/A /* notify locks once every 5 seconds.
1500N/A try to use rounded values. */
1500N/A next_alarm = (max_wait_time - now) % 5;
1500N/A if (next_alarm == 0)
1500N/A next_alarm = 5;
838N/A alarm(next_alarm);
1500N/A
1500N/A index_storage_lock_notify(&ctx->mbox->ibox,
1500N/A MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
1500N/A max_wait_time - now);
1500N/A }
1500N/A
1500N/A alarm(0);
1500N/A ctx->fcntl_locked = TRUE;
1500N/A return 1;
1500N/A}
1500N/A
1500N/Astatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
1500N/A time_t max_wait_time, int idx)
39N/A{
956N/A enum mbox_lock_type *lock_types;
956N/A enum mbox_lock_type type;
956N/A int i, ret = 0, lock_status;
956N/A
1431N/A ctx->lock_type = lock_type;
1431N/A
956N/A lock_types = lock_type == F_WRLCK ||
956N/A (lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ?
956N/A write_locks : read_locks;
956N/A for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) {
956N/A type = lock_types[i];
956N/A lock_status = lock_type != F_UNLCK;
941N/A
1007N/A if (ctx->lock_status[type] == lock_status)
1007N/A continue;
1007N/A ctx->lock_status[type] = lock_status;
1007N/A
1007N/A ret = lock_data[type].func(ctx, lock_type, max_wait_time);
1007N/A if (ret <= 0)
1007N/A break;
1007N/A }
1007N/A return ret;
1007N/A}
1007N/A
1007N/Astatic int mbox_update_locking(struct mbox_mailbox *mbox, int lock_type,
1007N/A bool *fcntl_locked_r)
1007N/A{
1100N/A struct mbox_lock_context ctx;
1407N/A time_t max_wait_time;
941N/A int ret, i;
941N/A bool drop_locks;
144N/A
941N/A *fcntl_locked_r = FALSE;
1100N/A
1100N/A index_storage_lock_notify_reset(&mbox->ibox);
1100N/A
1100N/A if (!lock_settings_initialized)
1100N/A mbox_init_lock_settings();
1100N/A
1100N/A if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) {
1100N/A /* read-only mbox stream. no need to lock. */
1100N/A i_assert(mbox->mbox_readonly);
1100N/A mbox->mbox_lock_type = lock_type;
941N/A return 1;
941N/A }
941N/A
941N/A max_wait_time = time(NULL) + lock_timeout;
941N/A
941N/A memset(&ctx, 0, sizeof(ctx));
941N/A ctx.mbox = mbox;
941N/A
941N/A if (mbox->mbox_lock_type == F_WRLCK) {
941N/A /* dropping to shared lock. first drop those that we
941N/A don't remove completely. */
941N/A for (i = 0; i < MBOX_LOCK_COUNT; i++)
941N/A ctx.lock_status[i] = 1;
941N/A for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
941N/A ctx.lock_status[read_locks[i]] = 0;
429N/A drop_locks = TRUE;
941N/A } else {
941N/A drop_locks = FALSE;
941N/A }
941N/A
941N/A mbox->mbox_lock_type = lock_type;
429N/A ret = mbox_lock_list(&ctx, lock_type, max_wait_time, 0);
941N/A if (ret <= 0) {
941N/A if (!drop_locks)
941N/A (void)mbox_unlock_files(&ctx);
941N/A if (ret == 0) {
941N/A mail_storage_set_error(&mbox->storage->storage,
1007N/A MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT);
1007N/A }
1234N/A return ret;
1007N/A }
1007N/A
1007N/A if (drop_locks) {
1007N/A /* dropping to shared lock: drop the locks that are only
1007N/A in write list */
1007N/A memset(ctx.lock_status, 0, sizeof(ctx.lock_status));
1007N/A for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++)
1007N/A ctx.lock_status[write_locks[i]] = 1;
1007N/A for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
1007N/A ctx.lock_status[read_locks[i]] = 0;
1007N/A
1007N/A mbox->mbox_lock_type = F_WRLCK;
1007N/A (void)mbox_lock_list(&ctx, F_UNLCK, 0, 0);
1007N/A mbox->mbox_lock_type = F_RDLCK;
1007N/A }
1007N/A
1007N/A *fcntl_locked_r = ctx.fcntl_locked;
1007N/A return 1;
1007N/A}
1007N/A
1007N/Aint mbox_lock(struct mbox_mailbox *mbox, int lock_type,
1007N/A unsigned int *lock_id_r)
1007N/A{
1007N/A bool fcntl_locked;
1007N/A int ret;
1007N/A
1007N/A /* allow only unlock -> shared/exclusive or exclusive -> shared */
1007N/A i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
941N/A i_assert(lock_type == F_RDLCK || mbox->mbox_lock_type != F_RDLCK);
941N/A
941N/A /* mbox must be locked before index */
144N/A i_assert(mbox->ibox.index->lock_type != F_WRLCK);
144N/A
1472N/A if (mbox->mbox_lock_type == F_UNLCK) {
1472N/A ret = mbox_update_locking(mbox, lock_type, &fcntl_locked);
1472N/A if (ret <= 0)
1472N/A return ret;
1352N/A
1516N/A if ((mbox->storage->storage.flags &
1890N/A MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) {
1890N/A nfs_flush_read_cache(mbox->path, mbox->mbox_fd,
1890N/A fcntl_locked ? lock_type : F_UNLCK,
1890N/A fcntl_locked);
1890N/A }
1352N/A
1472N/A mbox->mbox_lock_id += 2;
1352N/A }
1352N/A
1352N/A if (lock_type == F_RDLCK) {
1352N/A mbox->mbox_shared_locks++;
1352N/A *lock_id_r = mbox->mbox_lock_id;
1352N/A } else {
1352N/A mbox->mbox_excl_locks++;
2073N/A *lock_id_r = mbox->mbox_lock_id + 1;
1352N/A }
429N/A return 1;
429N/A}
144N/A
1386N/Astatic int mbox_unlock_files(struct mbox_lock_context *ctx)
1386N/A{
1386N/A int ret = 0;
1636N/A
1636N/A if (mbox_lock_list(ctx, F_UNLCK, 0, 0) < 0)
1636N/A ret = -1;
1636N/A
2073N/A ctx->mbox->mbox_lock_id += 2;
1636N/A ctx->mbox->mbox_lock_type = F_UNLCK;
2073N/A return ret;
1636N/A}
1636N/A
1636N/Aint mbox_unlock(struct mbox_mailbox *mbox, unsigned int lock_id)
1386N/A{
1636N/A struct mbox_lock_context ctx;
1636N/A bool fcntl_locked;
1636N/A int i;
1636N/A
2073N/A i_assert(mbox->mbox_lock_id == (lock_id & ~1));
1636N/A
2073N/A if (lock_id & 1) {
1636N/A /* dropping exclusive lock */
1636N/A i_assert(mbox->mbox_excl_locks > 0);
1636N/A if (--mbox->mbox_excl_locks > 0)
1386N/A return 0;
315N/A if (mbox->mbox_shared_locks > 0) {
315N/A /* drop to shared lock */
315N/A if (mbox_update_locking(mbox, F_RDLCK,
315N/A &fcntl_locked) < 0)
315N/A return -1;
315N/A return 0;
315N/A }
315N/A } else {
1636N/A /* dropping shared lock */
1636N/A i_assert(mbox->mbox_shared_locks > 0);
1636N/A if (--mbox->mbox_shared_locks > 0)
1636N/A return 0;
1636N/A if (mbox->mbox_excl_locks > 0)
1636N/A return 0;
2073N/A }
1636N/A /* all locks gone */
2073N/A
1636N/A memset(&ctx, 0, sizeof(ctx));
1636N/A ctx.mbox = mbox;
144N/A
838N/A for (i = 0; i < MBOX_LOCK_COUNT; i++)
838N/A ctx.lock_status[i] = 1;
838N/A
926N/A return mbox_unlock_files(&ctx);
926N/A}
926N/A