mbox-lock.c revision 6ef7e31619edfaa17ed044b45861d106a86191ef
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher/* Copyright (C) 2002 Timo Sirainen */
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher/* 0.1 .. 0.2msec */
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher/* lock methods to use in wanted order */
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl"
428db8a58c0c149d5efccc6d788f70916c1d34d7Jakub Hrozek/* lock timeout */
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher/* assume stale dotlock if mbox file hasn't changed for n seconds */
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher#define DEFAULT_DOTLOCK_CHANGE_TIMEOUT (5*60)
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke int (*func)(struct mbox_lock_context *ctx, int lock_type,
f3c85d900c4663854cc7bbae7d9f77867ed1f69bSumit Bosestatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
f3c85d900c4663854cc7bbae7d9f77867ed1f69bSumit Bosestatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
2a552e43581c74f51205c7141ec9f6e9542509f8Stephen Gallagherstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher { MBOX_LOCK_DOTLOCK, "dotlock", mbox_lock_dotlock },
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher { MBOX_LOCK_FCNTL, "fcntl", mbox_lock_fcntl },
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher { MBOX_LOCK_FLOCK, "flock", mbox_lock_flock },
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic bool lock_settings_initialized = FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1];
32381402a4a9afc003782c9e2301fc59c9bda2a9Yassir Elleystatic enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
dbfc407eef1d9ba2469687c3ffbe7fd8bb111d94Jakub Hrozekstatic int lock_timeout, dotlock_change_timeout;
4b6a0d0b3d42e5fdb457f47d9adfa5e66b160256Stephen Gallagherstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
5484044ea7bb632b915f706685fce509f6eacc48Jakub Hrozekstatic int mbox_unlock_files(struct mbox_lock_context *ctx);
6dcbfe52d5e64205c0d922f3e89add066b42c496Jakub Hrozekstatic void mbox_read_lock_methods(const char *str, const char *env,
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher const char *const *lock;
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) {
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek for (type = 0; lock_data[type].name != NULL; type++) {
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek if (strcasecmp(*lock, lock_data[type].name) == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("%s: Invalid value %s", env, *lock);
a3d176d116ceccd6a7547c128fab5df5cdd2c2b6Michal Zidek for (i = 0; i < dest; i++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("%s: Duplicated value %s", env, *lock);
558998ce664055a75595371118f818084d8f2b23Jan Cholasta const char *str;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (str == NULL) str = DEFAULT_READ_LOCK_METHODS;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS;
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay /* check that read/write list orders match. write_locks must contain
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina at least read_locks and possibly more. */
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (read_locks[r] != (enum mbox_lock_type)-1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("mbox read/write lock list settings are invalid. "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "Lock ordering must be the same with both, "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "and write locks must contain all read locks "
1746e8b8399da2a7a8da4aace186f66055ccfec1Jakub Hrozek "(and possibly more)");
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina lock_timeout = str == NULL ? DEFAULT_LOCK_TIMEOUT : atoi(str);
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozekstatic int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type)
4b39208286ca0351ee76d4e64e077e7ad5ca8568Jakub Hrozekstatic bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* get next index we wish to try locking. it's the one after
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher dotlocking. */
fe60346714a73ac3987f786731389320633dd245Pavel Březina for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) {
71e7918be3ca5d38794a16a17f6b4f19a24d51fcPavel Březina if (mbox_lock_list(ctx, ctx->lock_type, 0, i) <= 0) {
71e7918be3ca5d38794a16a17f6b4f19a24d51fcPavel Březina /* we couldn't get fd lock -
bbaba8b3ef9bc101863b8687f234f4ee956caacdPavel Březina it's really locked */
4bd20c075f0f187db0181dc53d00ab6cd47fdb4dJakub Hrozek index_storage_lock_notify(&ctx->mbox->ibox, stale ?
2dcf7b9b65df21f2aee6cdf051a7fbdef6dfe034Jakub Hrozekstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek if (file_dotlock_delete(&mbox->mbox_dotlock) <= 0) {
90afedb00608547ae1f32aa7aafd552c4b306909Jakub Hrozek mbox_set_syscall_error(mbox, "file_dotlock_delete()");
3a8f6b575f4019f21c9425a26f1b346c08a197aePavel Březina ret = file_dotlock_create(&set, mbox->path, 0, &mbox->mbox_dotlock);
2b62d5a414b8b7dba4f714dc5033e28dc4b1f4feJakub Hrozek mbox_set_syscall_error(mbox, "file_lock_dotlock()");
338af078fcc18126df939f20182acea7a646b7c8Michal Zidek "Timeout while waiting for lock");
b407fe0474a674bb42f0f42ab47c7f530a07a367Pavel Březina if (mbox_file_open_latest(ctx, lock_type) < 0)
bf54fbed126ec3d459af40ea370ffadacd31c76dJakub Hrozekstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
99f8be128274eba264ea1434a7eb2800bced5902Lukas Slebodnik if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek while (flock(ctx->mbox->mbox_fd, lock_type | LOCK_NB) < 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_set_syscall_error(ctx->mbox, "flock()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
cbff3fcdce5b0377a62fbe74f32e476efbf7ca9cNikolai Kondrashov if (mbox_file_open_latest(ctx, lock_type) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
a2e417f38c57ed87c956ddcecf4dafca93842b65Lukas Slebodnik while (lockf(ctx->mbox->mbox_fd, lock_type, 0) < 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_set_syscall_error(ctx->mbox, "lockf()");
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzkestatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
1d1a0a019d8d4d9ab0f51ada03604cd2cada287eSumit Bose unsigned int next_alarm;
96453f402831275a39d5fb89c33c9776e148d03fStephen Gallagher if (mbox_file_open_latest(ctx, lock_type) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* usually we're waiting here, but if we came from
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik mbox_lock_dotlock(), we just want to try locking */
1467daed400d6c186bd0c99c057c42e764309ff3Stephen Gallagher while (fcntl(ctx->mbox->mbox_fd, wait_type, &fl) < 0) {
15b266d9f14dad26da8678a79019749d0f69532eStephen Gallagher mbox_set_syscall_error(ctx->mbox, "fcntl()");
60e51fd2764291df2332f36ff478777627d92b57Sumit Bose /* notify locks once every 5 seconds.
18372712592b30638772afb5b7e15bfca92c2058Lukas Slebodnik try to use rounded values. */
cbff3fcdce5b0377a62fbe74f32e476efbf7ca9cNikolai Kondrashov index_storage_lock_notify(&ctx->mbox->ibox,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
a8d887323f83984679a7d9b827a70146656bb7b2Sumit Bose (lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ?
6398f22526303343193a18e514602f1af6fb29cbNikolai Kondrashov for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->lock_status[type] == lock_status)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = lock_data[type].func(ctx, lock_type, max_wait_time);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_update_locking(struct mbox_mailbox *mbox, int lock_type)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_storage_lock_notify_reset(&mbox->ibox);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) {
a5bb518446d5ce565d7ba819590a009cabb0b0b4Jakub Hrozek /* read-only mbox stream. no need to lock. */
59744cff6edb106ae799b2321cb8731edadf409aStephen Gallagher max_wait_time = time(NULL) + lock_timeout;
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce /* dropping to shared lock. first drop those that we
0ef783e186ef1c9f60e61a4e8e54c44cb366fdfePavel Březina don't remove completely. */
2cbdd12983eb85eddb90f64cfafb24eae5b448f4Jakub Hrozek for (i = 0; i < MBOX_LOCK_COUNT; i++)
50c9d542e8bf641412debaa82a4dcf67ddb72258Lukas Slebodnik for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = mbox_lock_list(&ctx, lock_type, max_wait_time, 0);
115de6d50f0d0bdd5745a5d8eb0d067be9128528Sumit Bose "Timeout while waiting for lock");
376eaf187c13c2a1eaea0ffbdd970b6b563ab74cPetr Cech /* dropping to shared lock: drop the locks that are only
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher in write list */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher memset(ctx.lock_status, 0, sizeof(ctx.lock_status));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
543676afec3c08fdc0a5a794976adc8dfdca974bJakub Hrozekint mbox_lock(struct mbox_mailbox *mbox, int lock_type,
543676afec3c08fdc0a5a794976adc8dfdca974bJakub Hrozek unsigned int *lock_id_r)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* allow only unlock -> shared/exclusive or exclusive -> shared */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(lock_type == F_RDLCK || mbox->mbox_lock_type != F_RDLCK);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* mbox must be locked before index */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(mbox->ibox.index->lock_type != F_WRLCK);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = mbox_update_locking(mbox, lock_type);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_unlock_files(struct mbox_lock_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_lock_list(ctx, F_UNLCK, 0, 0) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* make sure we don't keep mmap() between locks */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherint mbox_unlock(struct mbox_mailbox *mbox, unsigned int lock_id)
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek i_assert(mbox->mbox_lock_id == (lock_id & ~1));
558ec7d717735bb16c210c675c2cc5bee1da4576Lukas Slebodnik /* dropping exclusive lock */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* drop to shared lock */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* dropping shared lock */
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina /* all locks gone */
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce for (i = 0; i < MBOX_LOCK_COUNT; i++)