mbox-lock.c revision 64a29ac489669aee924a9ada2c60260e30451a02
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw/* 0.1 .. 0.2msec */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw/* lock methods to use in wanted order */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw/* lock timeout */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw/* assume stale dotlock if mbox file hasn't changed for n seconds */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw const char *name;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw int (*func)(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw { MBOX_LOCK_DOTLOCK_TRY, "dotlock_try", mbox_lock_dotlock_try },
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_unlock_files(struct mbox_lock_context *ctx);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic void mbox_read_lock_methods(const char *str, const char *env,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw const char *const *lock;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = 0; i < dest; i++) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* @UNSAFE */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic void mbox_init_lock_settings(void)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw const char *str;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* check that read/write list orders match. write_locks must contain
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw at least read_locks and possibly more. */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw i_fatal("mbox read/write lock list settings are invalid. "
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw "Lock ordering must be the same with both, "
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw "and write locks must contain all read locks "
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw "(and possibly more)");
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw lock_timeout = str == NULL ? MBOX_DEFAULT_LOCK_TIMEOUT : atoi(str);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* we could flush NFS file handle cache here if we wanted to
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw be sure that the file is latest, but mbox files get rarely
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw deleted and the flushing might cause errors (e.g. EBUSY for
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw trying to flush a /var/mail mountpoint) */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* get next index we wish to try locking. it's the one after
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw dotlocking. */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* we couldn't get fd lock -
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw it's really locked */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwmbox_lock_dotlock_int(struct mbox_lock_context *ctx, int lock_type, bool try)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw ret = file_dotlock_create(&set, mbox->path, 0, &mbox->mbox_dotlock);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw if (ret < 0) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw if (ret == 0) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw while (flock(ctx->mbox->mbox_fd, lock_type | LOCK_NB) < 0) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw unsigned int next_alarm;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* usually we're waiting here, but if we came from
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw mbox_lock_dotlock(), we just want to try locking */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* non-blocking lock trying failed */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* notify locks once every 5 seconds.
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw try to use rounded values. */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw (lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ?
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw ret = lock_data[type].func(ctx, lock_type, max_wait_time);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_update_locking(struct mbox_mailbox *mbox, int lock_type,
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* read-only mbox stream. no need to lock. */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* dropping to shared lock. first drop those that we
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw don't remove completely. */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = 0; i < MBOX_LOCK_COUNT; i++)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw if (ret <= 0) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw if (ret == 0) {
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* dropping to shared lock: drop the locks that are only
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw in write list */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw unsigned int *lock_id_r)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* allow only unlock -> shared/exclusive or exclusive -> shared */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw i_assert(lock_type == F_RDLCK || mbox->mbox_lock_type != F_RDLCK);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* mbox must be locked before index */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw ret = mbox_update_locking(mbox, lock_type, &fcntl_locked);
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwstatic int mbox_unlock_files(struct mbox_lock_context *ctx)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xwint mbox_unlock(struct mbox_mailbox *mbox, unsigned int lock_id)
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* dropping exclusive lock */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* drop to shared lock */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw return -1;
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* dropping shared lock */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw /* all locks gone */
d39a76e7b087a3d0927cbe6898dc0a6770fa6c68xw for (i = 0; i < MBOX_LOCK_COUNT; i++)