mail-index-lock.c revision a10f2e84184b91c584671cbbdcfb9330a5b48a9e
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale/* Copyright (C) 2003-2004 Timo Sirainen */
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller/*
7bde6e1907fc17b0083a6bcfad10bb74be6e4bcdTim Reddehase Locking should never fail or timeout. Exclusive locks must be kept as short
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller time as possible. Shared locks can be long living, so if we can't get
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller exclusive lock directly within 2 seconds, we'll replace the index file with
76e062333eb08ffc0c22578cc86ffc970c321fecTim Reddehase a copy of it. That means the shared lock holders can keep using the old file
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase while we're modifying the new file.
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase lock_id is used to figure out if acquired lock is still valid. When index
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase file is reopened, the lock_id can become invalid. It doesn't matter however,
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller as no-one's going to modify the old file anymore.
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller lock_id also tells if we're referring to shared or exclusive lock. This
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller allows us to drop back to shared locking once all exclusive locks are
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger dropped. Shared locks have even numbers, exclusive locks have odd numbers.
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger The number is increased by two every time the lock is dropped or index file
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger is reopened.
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger*/
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller#include "lib.h"
2fb61b27ca20bf21f93dd0866c01e58b429baeb5Eileen Bolloff#include "buffer.h"
2fb61b27ca20bf21f93dd0866c01e58b429baeb5Eileen Bolloff#include "mmap-util.h"
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale#include "write-full.h"
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase#include "mail-index-private.h"
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger#include <stdio.h>
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger#include <sys/stat.h>
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller#ifdef HAVE_FLOCK
a837d007b255d7a6cca7994e1e555aba95ce41cchenning mueller# include <sys/file.h>
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller#endif
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase#define MAIL_INDEX_LOCK_WAIT_TIME 120
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehaseint mail_index_lock_fd(struct mail_index *index, const char *path, int fd,
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase int lock_type, unsigned int timeout_secs)
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase{
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase int ret;
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase if (MAIL_INDEX_IS_IN_MEMORY(index))
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase return 1;
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase if (timeout_secs != 0)
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase alarm(MAIL_INDEX_LOCK_WAIT_TIME);
083f87068ffcf24df8b0154bfbca4ca5027f8ecehenning mueller
083f87068ffcf24df8b0154bfbca4ca5027f8ecehenning mueller switch (index->lock_method) {
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller case MAIL_INDEX_LOCK_FCNTL: {
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale#ifndef HAVE_FCNTL
ee5342a8882c2fc7631fcffb5497e6597747887cTim Reddehase i_fatal("fcntl() locks not supported");
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale#else
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase struct flock fl;
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase fl.l_type = lock_type;
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale fl.l_whence = SEEK_SET;
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase fl.l_start = 0;
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase fl.l_len = 0;
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale ret = fcntl(fd, timeout_secs ? F_SETLKW : F_SETLK, &fl);
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale if (timeout_secs != 0) alarm(0);
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase if (ret == 0)
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase return 1;
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase if (timeout_secs == 0 &&
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase (errno == EACCES || errno == EAGAIN)) {
71fdc30cc6e637d99cacb455537e7b8fbfe77395henning mueller /* locked by another process */
71fdc30cc6e637d99cacb455537e7b8fbfe77395henning mueller return 0;
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale }
d1860f0434d6460259d04f8952e55df38fe517f7Tim Reddehase
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale if (errno == EINTR) {
40c5626383ebd5e8cf11a636f864023a2aafcd6bDaniel Couto Vale /* most likely alarm hit, meaning we timeouted.
a837d007b255d7a6cca7994e1e555aba95ce41cchenning mueller even if not, we probably want to be killed
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger so stop blocking. */
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger errno = EAGAIN;
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller return 0;
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller }
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase mail_index_file_set_syscall_error(index, path, "fcntl()");
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase return -1;
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase#endif
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase }
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase case MAIL_INDEX_LOCK_FLOCK: {
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase#ifndef HAVE_FLOCK
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase i_fatal("flock() locks not supported "
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase "(see lock_method setting in config file)");
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase#else
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase int operation = timeout_secs != 0 ? 0 : LOCK_NB;
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase switch (lock_type) {
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase case F_RDLCK:
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase operation |= LOCK_SH;
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase break;
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase case F_WRLCK:
75c2a995e5c564f96cf5559145e59f89d6435ee1Tim Reddehase operation |= LOCK_EX;
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller break;
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller case F_UNLCK:
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase operation |= LOCK_UN;
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase break;
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase }
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase ret = flock(fd, operation);
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase if (timeout_secs != 0) alarm(0);
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase if (ret == 0)
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase return 1;
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase if (errno == EWOULDBLOCK || errno == EINTR) {
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase /* a) locked by another process,
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase b) timeouted */
2977875e15fdb6d84be990579c61cda0b6cbb5d0Tim Reddehase return 0;
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase }
435547b6b5df0f76cd04b09532341b07d0defeb1Tim Reddehase if (errno == ENOLCK) {
609a4ff9a0e4cb89a9f529703c81554fe9c34ff6Tim Reddehase /* Give a bit more helpful error message since this
609a4ff9a0e4cb89a9f529703c81554fe9c34ff6Tim Reddehase is the default locking method and it doesn't work
609a4ff9a0e4cb89a9f529703c81554fe9c34ff6Tim Reddehase with NFS. */
609a4ff9a0e4cb89a9f529703c81554fe9c34ff6Tim Reddehase mail_index_set_error(index,
3856b7bdf70cec553e0ca01ca823c7fc555a06f0Tim Reddehase "flock() failed with file %s: %m "
3856b7bdf70cec553e0ca01ca823c7fc555a06f0Tim Reddehase "(see lock_method setting in config file)",
3856b7bdf70cec553e0ca01ca823c7fc555a06f0Tim Reddehase path);
3856b7bdf70cec553e0ca01ca823c7fc555a06f0Tim Reddehase return -1;
3856b7bdf70cec553e0ca01ca823c7fc555a06f0Tim Reddehase }
609a4ff9a0e4cb89a9f529703c81554fe9c34ff6Tim Reddehase mail_index_file_set_syscall_error(index, path, "flock()");
3856b7bdf70cec553e0ca01ca823c7fc555a06f0Tim Reddehase return -1;
3856b7bdf70cec553e0ca01ca823c7fc555a06f0Tim Reddehase#endif
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase }
609a4ff9a0e4cb89a9f529703c81554fe9c34ff6Tim Reddehase case MAIL_INDEX_LOCK_DOTLOCK:
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase /* we shouldn't get here */
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase break;
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase }
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase i_unreached();
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase}
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehasestatic int mail_index_lock(struct mail_index *index, int lock_type,
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase unsigned int timeout_secs, int update_index,
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase unsigned int *lock_id_r)
a8ab4146c3238374bdd13a36b12d665cde57e078Tim Reddehase{
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller int ret, ret2;
519002bc41b20a069be1c669660e702f9bd4a593Eugen Kuksa
705933deb08bc4269e8c08d50143af3cb5c1c670henning mueller i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger
881c5d2df3b2375b1ed2093781643873e424fe2cTim Reddehase if (lock_type == F_RDLCK && index->lock_type != F_UNLCK) {
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger index->shared_lock_count++;
ac2169141f0b549fc8917a4b1d778f4ba3cab0bfJulian Kornberger *lock_id_r = index->lock_id;
ac2169141f0b549fc8917a4b1d778f4ba3cab0bfJulian Kornberger ret = 1;
aa056a2e5efb6505701a4e4a31bf2f7e71ff1738henning mueller } else if (lock_type == F_WRLCK && index->lock_type == F_WRLCK) {
ac2169141f0b549fc8917a4b1d778f4ba3cab0bfJulian Kornberger index->excl_lock_count++;
705933deb08bc4269e8c08d50143af3cb5c1c670henning mueller *lock_id_r = index->lock_id + 1;
519002bc41b20a069be1c669660e702f9bd4a593Eugen Kuksa ret = 1;
519002bc41b20a069be1c669660e702f9bd4a593Eugen Kuksa } else {
aa056a2e5efb6505701a4e4a31bf2f7e71ff1738henning mueller ret = 0;
aa056a2e5efb6505701a4e4a31bf2f7e71ff1738henning mueller }
aa056a2e5efb6505701a4e4a31bf2f7e71ff1738henning mueller
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger if (update_index && index->excl_lock_count == 0) {
705933deb08bc4269e8c08d50143af3cb5c1c670henning mueller i_assert(index->lock_type != F_WRLCK);
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger if ((ret2 = mail_index_reopen_if_needed(index)) < 0)
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger return -1;
705933deb08bc4269e8c08d50143af3cb5c1c670henning mueller if (ret > 0 && ret2 == 0) {
b5bf21df9d1d61069e6dc9e13569543d0b868bc8Daniel Couto Vale i_assert(lock_type == F_RDLCK);
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger i_assert(index->lock_type == F_RDLCK);
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger return 1;
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger }
aa056a2e5efb6505701a4e4a31bf2f7e71ff1738henning mueller ret = 0;
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger }
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger
ee5342a8882c2fc7631fcffb5497e6597747887cTim Reddehase if (ret > 0)
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase return 1;
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase if (index->lock_method == MAIL_INDEX_LOCK_DOTLOCK) {
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger /* FIXME: exclusive locking will rewrite the index file every
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger time. shouldn't really be needed.. reading doesn't require
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger locks then, though */
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller if (lock_type == F_WRLCK)
7ea9649883e1bbe8f2582db1a3c66af8b7206056henning mueller return 0;
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger if (update_index && index->lock_type == F_UNLCK) {
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger if (mail_index_reopen_if_needed(index) < 0)
b5bf21df9d1d61069e6dc9e13569543d0b868bc8Daniel Couto Vale return -1;
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger }
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger index->shared_lock_count++;
a274d776b3371051dcdd74b598182ce113ca5135Julian Kornberger index->lock_type = F_RDLCK;
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger *lock_id_r = index->lock_id;
b5bf21df9d1d61069e6dc9e13569543d0b868bc8Daniel Couto Vale return 1;
ec416eab0158cfe34b77cea4a11f8b84bc194a7aDaniel Couto Vale }
ec416eab0158cfe34b77cea4a11f8b84bc194a7aDaniel Couto Vale
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger if (lock_type == F_RDLCK || !index->log_locked) {
4952c0f2ffd64062becdc4efeb38446a904d7ec1Julian Kornberger ret = mail_index_lock_fd(index, index->filepath, index->fd,
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger lock_type, timeout_secs);
2e7a48951591a6eeda9a3ab4e14cd13012cb43eaJulian Kornberger } else {
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase /* this is kind of kludgy. we wish to avoid deadlocks while
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase trying to lock transaction log, but it can happen if our
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase process is holding transaction log lock and waiting for
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase index write lock, while the other process is holding index
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase read lock and waiting for transaction log lock.
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase we don't have a problem with grabbing read index lock
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase because the only way for it to block is if it's
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase write-locked, which isn't allowed unless transaction log
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase is also locked.
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase so, the workaround for this problem is that we simply try
6116d75120b5db0bcbc4a11abed8d0254ec85b8eTim Reddehase locking once. if it doesn't work, just rewrite the file.
58721b9d3a8cd6a624269ddf507f80af4417c9bdhenning mueller hopefully there won't be any other deadlocking issues. :) */
ret = mail_index_lock_fd(index, index->filepath, index->fd,
lock_type, 0);
}
if (ret <= 0)
return ret;
if (index->lock_type == F_UNLCK)
index->lock_id += 2;
index->lock_type = lock_type;
if (lock_type == F_RDLCK) {
index->shared_lock_count++;
*lock_id_r = index->lock_id;
} else {
index->excl_lock_count++;
*lock_id_r = index->lock_id + 1;
}
return 1;
}
int mail_index_lock_shared(struct mail_index *index, int update_index,
unsigned int *lock_id_r)
{
int ret;
ret = mail_index_lock(index, F_RDLCK, MAIL_INDEX_LOCK_SECS,
update_index, lock_id_r);
if (ret > 0)
return 0;
if (ret < 0)
return -1;
mail_index_set_error(index,
"Timeout while waiting for shared lock for index file %s",
index->filepath);
index->index_lock_timeout = TRUE;
return -1;
}
static int mail_index_copy(struct mail_index *index)
{
struct mail_index_map *map = index->map;
unsigned int base_size;
const char *path;
int ret, fd;
i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
fd = mail_index_create_tmp_file(index, &path);
if (fd == -1)
return -1;
/* write base header */
base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr));
ret = write_full(fd, &map->hdr, base_size);
if (ret == 0) {
/* write extended headers */
ret = write_full(fd, CONST_PTR_OFFSET(map->hdr_base, base_size),
map->hdr.header_size - base_size);
}
if (ret < 0 || write_full(fd, map->records, map->records_count *
map->hdr.record_size) < 0) {
mail_index_file_set_syscall_error(index, path, "write_full()");
(void)close(fd);
(void)unlink(path);
fd = -1;
} else {
i_assert(index->copy_lock_path == NULL);
index->copy_lock_path = i_strdup(path);
}
return fd;
}
static int mail_index_lock_exclusive_copy(struct mail_index *index)
{
int fd, old_lock_type;
i_assert(index->log_locked);
if (index->copy_lock_path != NULL) {
index->excl_lock_count++;
return 0;
}
i_assert(index->excl_lock_count == 0);
/* copy the index to index.tmp and use it */
fd = mail_index_copy(index);
if (fd == -1)
return -1;
old_lock_type = index->lock_type;
index->lock_type = F_WRLCK;
index->excl_lock_count++;
if (mail_index_reopen(index, fd) < 0) {
i_assert(index->excl_lock_count == 1);
(void)close(fd);
if (unlink(index->copy_lock_path) < 0) {
mail_index_file_set_syscall_error(index,
index->copy_lock_path,
"unlink()");
}
i_free(index->copy_lock_path);
index->copy_lock_path = NULL;
index->lock_type = old_lock_type;
index->excl_lock_count = 0;
return -1;
}
return 0;
}
int mail_index_lock_exclusive(struct mail_index *index,
unsigned int *lock_id_r)
{
int ret;
/* exclusive transaction log lock protects exclusive locking
for the main index file */
i_assert(index->log_locked);
/* if header size is smaller than what we have, we'll have to recreate
the index to grow it. so don't even try regular locking. */
if (index->map->hdr.base_header_size >= sizeof(*index->hdr) ||
index->excl_lock_count > 0) {
/* wait two seconds for exclusive lock */
ret = mail_index_lock(index, F_WRLCK, 2, TRUE, lock_id_r);
if (ret > 0)
return 0;
if (ret < 0)
return -1;
}
if (mail_index_lock_exclusive_copy(index) < 0)
return -1;
*lock_id_r = index->lock_id + 1;
return 0;
}
static int mail_index_copy_lock_finish(struct mail_index *index)
{
if (fsync(index->fd) < 0) {
mail_index_file_set_syscall_error(index, index->copy_lock_path,
"fsync()");
return -1;
}
if (rename(index->copy_lock_path, index->filepath) < 0) {
mail_index_set_error(index, "rename(%s, %s) failed: %m",
index->copy_lock_path, index->filepath);
return -1;
}
i_free(index->copy_lock_path);
index->copy_lock_path = NULL;
return 0;
}
static int mail_index_write_map_over(struct mail_index *index)
{
struct mail_index_map *map = index->map;
unsigned int base_size;
/* write records. */
if (map->write_seq_first != 0) {
size_t rec_offset =
(map->write_seq_first-1) * map->hdr.record_size;
if (pwrite_full(index->fd,
CONST_PTR_OFFSET(map->records, rec_offset),
(map->write_seq_last -
map->write_seq_first + 1) *
map->hdr.record_size,
map->hdr.header_size + rec_offset) < 0)
return -1;
}
/* write base header */
base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr));
if (pwrite_full(index->fd, &map->hdr, base_size, 0) < 0)
return -1;
/* write extended headers */
if (pwrite_full(index->fd, CONST_PTR_OFFSET(map->hdr_base, base_size),
map->hdr.header_size - base_size, base_size) < 0)
return -1;
return 0;
}
static void mail_index_write_map(struct mail_index *index)
{
struct mail_index_map *map = index->map;
int fd;
if (map->write_atomic || index->copy_lock_path != NULL ||
index->fd == -1) {
/* write by recreating the index */
i_assert(index->log_locked);
if (index->copy_lock_path != NULL) {
/* new mapping replaces the old */
(void)unlink(index->copy_lock_path);
i_free(index->copy_lock_path);
index->copy_lock_path = NULL;
}
if (!MAIL_INDEX_IS_IN_MEMORY(index)) {
fd = mail_index_copy(index);
if (fd == -1)
mail_index_set_inconsistent(index);
else
(void)close(fd);
}
} else {
/* write the modified parts. header is small enough to be
always written, write_seq_* specifies the record range. */
if (mail_index_write_map_over(index) < 0)
mail_index_set_inconsistent(index);
}
map->write_to_disk = FALSE;
map->write_atomic = FALSE;
map->write_seq_first = map->write_seq_last = 0;
}
static void mail_index_excl_unlock_finish(struct mail_index *index)
{
if (index->map != NULL && index->map->write_to_disk)
mail_index_write_map(index);
if (index->shared_lock_count > 0 &&
index->lock_method != MAIL_INDEX_LOCK_DOTLOCK) {
/* leave ourself shared locked. */
(void)mail_index_lock_fd(index, index->filepath, index->fd,
F_RDLCK, 0);
i_assert(index->lock_type == F_WRLCK);
index->lock_type = F_RDLCK;
}
if (index->copy_lock_path != NULL) {
i_assert(index->log_locked);
if (mail_index_copy_lock_finish(index) < 0)
mail_index_set_inconsistent(index);
}
}
void mail_index_unlock(struct mail_index *index, unsigned int lock_id)
{
if ((lock_id & 1) == 0) {
/* shared lock */
if (!mail_index_is_locked(index, lock_id)) {
/* unlocking some older generation of the index file.
we've already closed the file so just ignore this. */
return;
}
i_assert(index->shared_lock_count > 0);
index->shared_lock_count--;
} else {
/* exclusive lock */
i_assert(lock_id == index->lock_id + 1);
i_assert(index->excl_lock_count > 0);
if (--index->excl_lock_count == 0)
mail_index_excl_unlock_finish(index);
}
if (index->shared_lock_count == 0 && index->excl_lock_count == 0) {
index->lock_id += 2;
index->lock_type = F_UNLCK;
if (index->lock_method != MAIL_INDEX_LOCK_DOTLOCK) {
(void)mail_index_lock_fd(index, index->filepath,
index->fd, F_UNLCK, 0);
}
}
}
int mail_index_is_locked(struct mail_index *index, unsigned int lock_id)
{
if ((index->lock_id ^ lock_id) <= 1) {
i_assert(index->lock_type != F_UNLCK);
return TRUE;
}
return FALSE;
}