mbox-lock.c revision 0c450f355c18905dd18d1559bf815d6b6332cd26
/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "restrict-access.h"
#include "nfs-workarounds.h"
#include "mail-index-private.h"
#include "mbox-storage.h"
#include "mbox-file.h"
#include "mbox-lock.h"
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef HAVE_FLOCK
#endif
/* 0.1 .. 0.2msec */
/* lock methods to use in wanted order */
#define DEFAULT_READ_LOCK_METHODS "fcntl"
#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl"
/* lock timeout */
/* assume stale dotlock if mbox file hasn't changed for n seconds */
#define DEFAULT_DOTLOCK_CHANGE_TIMEOUT (120)
enum mbox_lock_type {
};
enum mbox_dotlock_op {
};
struct mbox_lock_context {
struct mbox_mailbox *mbox;
int lock_status[MBOX_LOCK_COUNT];
bool checked_file;
int lock_type;
bool dotlock_last_stale;
bool fcntl_locked;
bool using_privileges;
};
struct mbox_lock_data {
enum mbox_lock_type type;
const char *name;
};
#ifdef HAVE_FLOCK
#else
# define mbox_lock_flock NULL
#endif
#ifdef HAVE_LOCKF
#else
# define mbox_lock_lockf NULL
#endif
struct mbox_lock_data lock_data[] = {
};
static bool lock_settings_initialized = FALSE;
static int lock_timeout, dotlock_change_timeout;
enum mbox_lock_type *locks)
{
enum mbox_lock_type type;
const char *const *lock;
int i, dest;
break;
}
}
i_fatal("%s: Support for lock type %s "
}
for (i = 0; i < dest; i++) {
}
/* @UNSAFE */
}
}
static void mbox_init_lock_settings(void)
{
const char *str;
int r, w;
at least read_locks and possibly more. */
break;
if (read_locks[r] == write_locks[w])
r++;
}
"Lock ordering must be the same with both, "
"and write locks must contain all read locks "
"(and possibly more)");
}
}
{
return 0;
/* we could flush NFS file handle cache here if we wanted to
be sure that the file is latest, but mbox files get rarely
deleted and the flushing might cause errors (e.g. EBUSY for
return -1;
}
}
if (mbox_file_open(mbox) < 0)
return -1;
}
return 0;
}
{
enum mbox_lock_type *lock_types;
int i;
if (ctx->using_privileges)
/* get next index we wish to try locking. it's the one after
dotlocking. */
if (lock_types[i] == MBOX_LOCK_DOTLOCK)
break;
}
i++;
/* we couldn't get fd lock -
it's really locked */
return FALSE;
}
}
}
if (ctx->using_privileges) {
if (restrict_access_use_priv_gid() < 0) {
/* shouldn't get here */
return FALSE;
}
}
return TRUE;
}
struct dotlock_settings *set,
enum mbox_dotlock_op op)
{
if (orig_dir_fd == -1) {
i_error("open(.) failed: %m");
return -1;
}
/* allow dotlocks to be created only for files we can read while we're
unprivileged. to make sure there are no race conditions we first
have to chdir to the mbox file's directory and then use relative
paths. unless this is done, users could:
- create *.lock files to any directory writable by the
privileged group
- DoS other users by dotlocking their mailboxes infinitely
*/
/* already relative */
} else {
(void)close(orig_dir_fd);
return -1;
}
fname++;
}
if (op == MBOX_DOTLOCK_OP_LOCK) {
return -1;
}
}
if (restrict_access_use_priv_gid() < 0) {
(void)close(orig_dir_fd);
return -1;
}
switch (op) {
case MBOX_DOTLOCK_OP_LOCK:
/* we're now privileged - avoid doing as much as possible */
if (ret > 0)
break;
case MBOX_DOTLOCK_OP_UNLOCK:
/* we're now privileged - avoid doing as much as possible */
break;
case MBOX_DOTLOCK_OP_TOUCH:
ret = -1;
} else {
}
break;
}
if (fchdir(orig_dir_fd) < 0)
i_error("fchdir() failed: %m");
(void)close(orig_dir_fd);
return ret;
}
static int
{
struct dotlock_settings set;
int ret;
if (!mbox->mbox_dotlocked)
return 1;
if (!mbox->mbox_used_privileges)
else {
}
if (ret <= 0) {
ret = -1;
}
return 1;
}
if (mbox->mbox_dotlocked)
return 1;
/* try again, this time with extra privileges */
}
if (ret < 0) {
return 1;
return -1;
}
if (ret == 0) {
return 0;
}
return -1;
return 1;
}
{
}
{
}
#ifdef HAVE_FLOCK
{
return -1;
return 1;
else
last_notify = 0;
if (errno != EWOULDBLOCK) {
return -1;
}
if (now >= max_wait_time)
return 0;
if (now != last_notify) {
max_wait_time - now);
}
}
return 1;
}
#endif
#ifdef HAVE_LOCKF
{
return -1;
return 1;
else
last_notify = 0;
return -1;
}
if (now >= max_wait_time)
return 0;
if (now != last_notify) {
max_wait_time - now);
}
}
return 1;
}
#endif
{
unsigned int next_alarm;
int wait_type;
return -1;
return 1;
if (max_wait_time == 0) {
/* usually we're waiting here, but if we came from
mbox_lock_dotlock(), we just want to try locking */
} else {
if (now >= max_wait_time)
alarm(1);
else
}
/* non-blocking lock trying failed */
return 0;
}
alarm(0);
return -1;
}
if (now >= max_wait_time) {
alarm(0);
return 0;
}
/* notify locks once every 5 seconds.
try to use rounded values. */
if (next_alarm == 0)
next_alarm = 5;
max_wait_time - now);
}
alarm(0);
return 1;
}
{
enum mbox_lock_type *lock_types;
enum mbox_lock_type type;
int i, ret = 0, lock_status;
type = lock_types[i];
continue;
if (ret <= 0)
break;
}
return ret;
}
bool *fcntl_locked_r)
{
struct mbox_lock_context ctx;
int ret, i;
bool drop_locks;
*fcntl_locked_r = FALSE;
/* read-only mbox stream. no need to lock. */
return 1;
}
/* dropping to shared lock. first drop those that we
don't remove completely. */
for (i = 0; i < MBOX_LOCK_COUNT; i++)
drop_locks = TRUE;
} else {
drop_locks = FALSE;
}
if (ret <= 0) {
if (!drop_locks)
(void)mbox_unlock_files(&ctx);
if (ret == 0) {
}
return ret;
}
if (drop_locks) {
/* dropping to shared lock: drop the locks that are only
in write list */
}
return 1;
}
unsigned int *lock_id_r)
{
bool fcntl_locked;
int ret;
/* mbox must be locked before index */
if (ret <= 0)
return ret;
MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) {
if (fcntl_locked) {
} else {
}
}
}
} else {
mbox->mbox_excl_locks++;
}
return 1;
}
{
int ret = 0;
ret = -1;
return ret;
}
{
struct mbox_lock_context ctx;
bool fcntl_locked;
int i;
if (lock_id & 1) {
/* dropping exclusive lock */
if (--mbox->mbox_excl_locks > 0)
return 0;
if (mbox->mbox_shared_locks > 0) {
/* drop to shared lock */
&fcntl_locked) < 0)
return -1;
return 0;
}
} else {
/* dropping shared lock */
if (--mbox->mbox_shared_locks > 0)
return 0;
if (mbox->mbox_excl_locks > 0)
return 0;
}
/* all locks gone */
for (i = 0; i < MBOX_LOCK_COUNT; i++)
return mbox_unlock_files(&ctx);
}
{
return;
if (!mbox->mbox_used_privileges)
else {
}
}