mbox-lock.c revision c000c8eca8f24b2a0c76393ec4bbf76a505a4983
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "lib.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "eacces-error.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "restrict-access.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "nfs-workarounds.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "mail-index-private.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "mbox-storage.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "istream-raw-mbox.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "mbox-file.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include "mbox-lock.h"
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include <time.h>
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include <stdlib.h>
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include <unistd.h>
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include <fcntl.h>
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#include <sys/stat.h>
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen#include <grp.h>
8b79a8fec4127c754509d65faae1fe7f09515365Christian Brauner
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen#ifdef HAVE_FLOCK
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur# include <sys/file.h>
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#endif
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
95ee490bbdb97ab2b4f1dfa63a0a26e0dd1c2f17Stéphane Graber/* 0.1 .. 0.2msec */
95ee490bbdb97ab2b4f1dfa63a0a26e0dd1c2f17Stéphane Graber#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onurenum mbox_lock_type {
948955a2d6f8e4e28bceada9666c5831de4a6bb8Stéphane Graber MBOX_LOCK_DOTLOCK,
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur MBOX_LOCK_DOTLOCK_TRY,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MBOX_LOCK_FCNTL,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MBOX_LOCK_FLOCK,
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engen MBOX_LOCK_LOCKF,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MBOX_LOCK_COUNT
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen};
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
0b98289ef95e83fcd493ba6688e57aa8b4fb53f7Andrey Mazoenum mbox_dotlock_op {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MBOX_DOTLOCK_OP_LOCK,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MBOX_DOTLOCK_OP_UNLOCK,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MBOX_DOTLOCK_OP_TOUCH
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen};
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engenstruct mbox_lock_context {
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engen struct mbox_mailbox *mbox;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen int lock_status[MBOX_LOCK_COUNT];
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen bool checked_file;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur int lock_type;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen bool dotlock_last_stale;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen bool fcntl_locked;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen bool using_privileges;
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen};
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engenstruct mbox_lock_data {
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen enum mbox_lock_type type;
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen const char *name;
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen int (*func)(struct mbox_lock_context *ctx, int lock_type,
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen time_t max_wait_time);
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engen};
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engenstatic int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen time_t max_wait_time);
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engenstatic int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen time_t max_wait_time);
103a2fc0729727d74470a366a1ea942d2cda2326Serge Hallynstatic int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur time_t max_wait_time);
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#ifdef HAVE_FLOCK
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onurstatic int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
0b98289ef95e83fcd493ba6688e57aa8b4fb53f7Andrey Mazo time_t max_wait_time);
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#else
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur# define mbox_lock_flock NULL
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen#endif
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen#ifdef HAVE_LOCKF
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engenstatic int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur time_t max_wait_time);
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur#else
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur# define mbox_lock_lockf NULL
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen#endif
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engenstatic struct mbox_lock_data lock_data[] = {
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur { MBOX_LOCK_DOTLOCK, "dotlock", mbox_lock_dotlock },
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen { MBOX_LOCK_DOTLOCK_TRY, "dotlock_try", mbox_lock_dotlock_try },
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen { MBOX_LOCK_FCNTL, "fcntl", mbox_lock_fcntl },
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen { MBOX_LOCK_FLOCK, "flock", mbox_lock_flock },
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen { MBOX_LOCK_LOCKF, "lockf", mbox_lock_lockf },
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur { 0, NULL, NULL }
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engen};
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graber
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graberstatic int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engen time_t max_wait_time, int idx);
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engenstatic int mbox_unlock_files(struct mbox_lock_context *ctx);
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onurstatic void mbox_read_lock_methods(const char *str, const char *env,
103a2fc0729727d74470a366a1ea942d2cda2326Serge Hallyn enum mbox_lock_type *locks)
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur{
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur enum mbox_lock_type type;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur const char *const *lock;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur int i, dest;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur for (lock = t_strsplit(str, " "), dest = 0; *lock != NULL; lock++) {
540f932aeb28274e8e7ea1e8f3a8e5889b88e1d6Stéphane Graber for (type = 0; lock_data[type].name != NULL; type++) {
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur if (strcasecmp(*lock, lock_data[type].name) == 0) {
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur type = lock_data[type].type;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur break;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur }
3afbcc4600ab689dacbec7657dee1aa4b41de08eS.Çağlar Onur }
b130964dd7faf19abc7afde7eebe7905a0fe8661S.Çağlar Onur if (lock_data[type].name == NULL)
b130964dd7faf19abc7afde7eebe7905a0fe8661S.Çağlar Onur i_fatal("%s: Invalid value %s", env, *lock);
b130964dd7faf19abc7afde7eebe7905a0fe8661S.Çağlar Onur if (lock_data[type].func == NULL) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen i_fatal("%s: Support for lock type %s "
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur "not compiled into binary", env, *lock);
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur }
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur for (i = 0; i < dest; i++) {
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur if (locks[i] == type)
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur i_fatal("%s: Duplicated value %s", env, *lock);
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur }
3afbcc4600ab689dacbec7657dee1aa4b41de08eS.Çağlar Onur
b130964dd7faf19abc7afde7eebe7905a0fe8661S.Çağlar Onur /* @UNSAFE */
b130964dd7faf19abc7afde7eebe7905a0fe8661S.Çağlar Onur locks[dest++] = type;
b130964dd7faf19abc7afde7eebe7905a0fe8661S.Çağlar Onur }
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur locks[dest] = (enum mbox_lock_type)-1;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur}
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onurstatic void mbox_init_lock_settings(struct mbox_storage *storage)
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur{
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1];
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur int r, w;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur mbox_read_lock_methods(storage->set->mbox_read_locks,
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur "mbox_read_locks", read_locks);
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur mbox_read_lock_methods(storage->set->mbox_write_locks,
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engen "mbox_write_locks", write_locks);
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graber
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur /* check that read/write list orders match. write_locks must contain
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur at least read_locks and possibly more. */
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen for (r = w = 0; write_locks[w] != (enum mbox_lock_type)-1; w++) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (read_locks[r] == (enum mbox_lock_type)-1)
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen break;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (read_locks[r] == write_locks[w])
4012c89148c42dc7c15809bec348746681f96eddStéphane Graber r++;
4012c89148c42dc7c15809bec348746681f96eddStéphane Graber }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (read_locks[r] != (enum mbox_lock_type)-1) {
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur i_fatal("mbox read/write lock list settings are invalid. "
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur "Lock ordering must be the same with both, "
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen "and write locks must contain all read locks "
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur "(and possibly more)");
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur storage->read_locks = p_new(storage->storage.pool,
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen enum mbox_lock_type, MBOX_LOCK_COUNT+1);
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen memcpy(storage->read_locks, read_locks,
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur sizeof(*storage->read_locks) * (MBOX_LOCK_COUNT+1));
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur storage->write_locks = p_new(storage->storage.pool,
36eaa694151aff72a74987034d5f0eeb4261951cDwight Engen enum mbox_lock_type, MBOX_LOCK_COUNT+1);
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen memcpy(storage->write_locks, write_locks,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen sizeof(*storage->write_locks) * (MBOX_LOCK_COUNT+1));
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen storage->lock_settings_initialized = TRUE;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen}
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engenstatic int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type)
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen{
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen struct mbox_mailbox *mbox = ctx->mbox;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen struct stat st;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (ctx->checked_file || lock_type == F_UNLCK)
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen return 0;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (mbox->mbox_fd != -1) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen /* we could flush NFS file handle cache here if we wanted to
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graber be sure that the file is latest, but mbox files get rarely
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graber deleted and the flushing might cause errors (e.g. EBUSY for
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graber trying to flush a /var/mail mountpoint) */
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen if (nfs_safe_stat(mailbox_get_path(&mbox->box), &st) < 0) {
448e272de5b391d3cd2d74b7ac6d7059ec767130Qiang Huang if (errno == ENOENT)
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen mailbox_set_deleted(&mbox->box);
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen else
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen mbox_set_syscall_error(mbox, "stat()");
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen return -1;
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen }
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen
ea7e3744e70e9f29ef6962b70121d785b310317aArjun Sreedharan if (st.st_ino != mbox->mbox_ino ||
ea7e3744e70e9f29ef6962b70121d785b310317aArjun Sreedharan !CMP_DEV_T(st.st_dev, mbox->mbox_dev))
ea7e3744e70e9f29ef6962b70121d785b310317aArjun Sreedharan mbox_file_close(mbox);
ea7e3744e70e9f29ef6962b70121d785b310317aArjun Sreedharan }
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graber if (mbox->mbox_fd == -1) {
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen if (mbox_file_open(mbox) < 0)
293ec758831aa307d834aa6dc1bee36bc604e674Dwight Engen return -1;
d028235de9ec7664e1c2c904c541a447a768997aStéphane Graber }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen ctx->checked_file = TRUE;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen return 0;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur}
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engenstatic bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen{
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen struct mbox_lock_context *ctx = context;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen enum mbox_lock_type *lock_types;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen int i;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (ctx->using_privileges)
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur restrict_access_drop_priv_gid();
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (stale && !ctx->dotlock_last_stale) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen /* get next index we wish to try locking. it's the one after
585ed6b1b64083e11f2196bc5913b096428dacc2Stéphane Graber dotlocking. */
585ed6b1b64083e11f2196bc5913b096428dacc2Stéphane Graber lock_types = ctx->lock_type == F_WRLCK ||
585ed6b1b64083e11f2196bc5913b096428dacc2Stéphane Graber (ctx->lock_type == F_UNLCK &&
585ed6b1b64083e11f2196bc5913b096428dacc2Stéphane Graber ctx->mbox->mbox_lock_type == F_WRLCK) ?
585ed6b1b64083e11f2196bc5913b096428dacc2Stéphane Graber ctx->mbox->storage->write_locks :
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen ctx->mbox->storage->read_locks;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (lock_types[i] == MBOX_LOCK_DOTLOCK)
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen break;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (lock_types[i] != (enum mbox_lock_type)-1 &&
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen lock_types[i+1] != (enum mbox_lock_type)-1) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen i++;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (mbox_lock_list(ctx, ctx->lock_type, 0, i) <= 0) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen /* we couldn't get fd lock -
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen it's really locked */
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen ctx->dotlock_last_stale = TRUE;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen return FALSE;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen (void)mbox_lock_list(ctx, F_UNLCK, 0, i);
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen }
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur ctx->dotlock_last_stale = stale;
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen index_storage_lock_notify(&ctx->mbox->box, stale ?
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE :
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen secs_left);
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (ctx->using_privileges) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen if (restrict_access_use_priv_gid() < 0) {
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen /* shouldn't get here */
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen return FALSE;
03fadd1656ce2bf994fc30617c5bb795803e0fdeStéphane Graber }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen }
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen return TRUE;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur}
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onurstatic int mbox_dotlock_privileged_op(struct mbox_mailbox *mbox,
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur struct dotlock_settings *set,
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen enum mbox_dotlock_op op)
beb6d93ee2b449ae2ea53125be2f198d15d8f8e8Dwight Engen{
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur const char *box_path, *dir, *fname;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur int ret = -1, orig_dir_fd, orig_errno;
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur
orig_dir_fd = open(".", O_RDONLY);
if (orig_dir_fd == -1) {
mail_storage_set_critical(&mbox->storage->storage,
"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
*/
box_path = mailbox_get_path(&mbox->box);
fname = strrchr(box_path, '/');
if (fname == NULL) {
/* already relative */
fname = box_path;
} else {
dir = t_strdup_until(box_path, fname);
if (chdir(dir) < 0) {
mail_storage_set_critical(&mbox->storage->storage,
"chdir(%s) failed: %m", dir);
(void)close(orig_dir_fd);
return -1;
}
fname++;
}
if (op == MBOX_DOTLOCK_OP_LOCK) {
if (access(fname, R_OK) < 0) {
mail_storage_set_critical(&mbox->storage->storage,
"access(%s) failed: %m", box_path);
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 */
ret = file_dotlock_create(set, fname, 0, &mbox->mbox_dotlock);
if (ret > 0)
mbox->mbox_used_privileges = TRUE;
else if (ret < 0 && errno == EACCES) {
const char *errmsg =
eacces_error_get_creating("file_dotlock_create",
fname);
mail_storage_set_critical(&mbox->storage->storage,
"%s", errmsg);
} else {
mbox_set_syscall_error(mbox, "file_dotlock_create()");
}
break;
case MBOX_DOTLOCK_OP_UNLOCK:
/* we're now privileged - avoid doing as much as possible */
ret = file_dotlock_delete(&mbox->mbox_dotlock);
if (ret < 0)
mbox_set_syscall_error(mbox, "file_dotlock_delete()");
mbox->mbox_used_privileges = FALSE;
break;
case MBOX_DOTLOCK_OP_TOUCH:
ret = file_dotlock_touch(mbox->mbox_dotlock);
if (ret < 0)
mbox_set_syscall_error(mbox, "file_dotlock_touch()");
break;
}
orig_errno = errno;
restrict_access_drop_priv_gid();
if (fchdir(orig_dir_fd) < 0) {
mail_storage_set_critical(&mbox->storage->storage,
"fchdir() failed: %m");
}
(void)close(orig_dir_fd);
errno = orig_errno;
return ret;
}
static void
mbox_dotlock_log_eacces_error(struct mbox_mailbox *mbox, const char *path)
{
const char *dir, *errmsg;
struct stat st;
const struct group *group;
int orig_errno = errno;
errmsg = eacces_error_get_creating("file_dotlock_create", path);
dir = strrchr(path, '/');
dir = dir == NULL ? "." : t_strdup_until(path, dir);
/* allow privileged locking for
a) user's own INBOX,
b) another user's shared INBOX, and
c) anything called INBOX (in inbox=no namespace) */
if (!mbox->box.inbox_any && strcmp(mbox->box.name, "INBOX") != 0) {
mail_storage_set_critical(&mbox->storage->storage,
"%s (not INBOX -> no privileged locking)", errmsg);
} else if (!mbox->mbox_privileged_locking) {
dir = mailbox_list_get_path(mbox->box.list, NULL,
MAILBOX_LIST_PATH_TYPE_DIR);
mail_storage_set_critical(&mbox->storage->storage,
"%s (under root dir %s -> no privileged locking)",
errmsg, dir);
} else if (stat(dir, &st) == 0 &&
(st.st_mode & 02) == 0 && /* not world-writable */
(st.st_mode & 020) != 0) { /* group-writable */
group = getgrgid(st.st_gid);
mail_storage_set_critical(&mbox->storage->storage,
"%s (set mail_privileged_group=%s)", errmsg,
group == NULL ? dec2str(st.st_gid) : group->gr_name);
} else {
mail_storage_set_critical(&mbox->storage->storage,
"%s (nonstandard permissions in %s)", errmsg, dir);
}
errno = orig_errno;
}
static int
mbox_lock_dotlock_int(struct mbox_lock_context *ctx, int lock_type, bool try)
{
struct mbox_mailbox *mbox = ctx->mbox;
struct dotlock_settings set;
int ret;
if (lock_type == F_UNLCK) {
if (!mbox->mbox_dotlocked)
return 1;
if (!mbox->mbox_used_privileges) {
if (file_dotlock_delete(&mbox->mbox_dotlock) <= 0) {
mbox_set_syscall_error(mbox,
"file_dotlock_delete()");
}
} else {
ctx->using_privileges = TRUE;
(void)mbox_dotlock_privileged_op(mbox, NULL,
MBOX_DOTLOCK_OP_UNLOCK);
ctx->using_privileges = FALSE;
}
mbox->mbox_dotlocked = FALSE;
return 1;
}
if (mbox->mbox_dotlocked)
return 1;
ctx->dotlock_last_stale = -1;
memset(&set, 0, sizeof(set));
set.use_excl_lock = mbox->storage->storage.set->dotlock_use_excl;
set.nfs_flush = mbox->storage->storage.set->mail_nfs_storage;
set.timeout = mail_storage_get_lock_timeout(&mbox->storage->storage,
mbox->storage->set->mbox_lock_timeout);
set.stale_timeout = mbox->storage->set->mbox_dotlock_change_timeout;
set.callback = dotlock_callback;
set.context = ctx;
ret = file_dotlock_create(&set, mailbox_get_path(&mbox->box), 0,
&mbox->mbox_dotlock);
if (ret >= 0) {
/* success / timeout */
} else if (errno == EACCES && restrict_access_have_priv_gid() &&
mbox->mbox_privileged_locking) {
/* try again, this time with extra privileges */
ret = mbox_dotlock_privileged_op(mbox, &set,
MBOX_DOTLOCK_OP_LOCK);
} else if (errno == EACCES)
mbox_dotlock_log_eacces_error(mbox, mailbox_get_path(&mbox->box));
else
mbox_set_syscall_error(mbox, "file_dotlock_create()");
if (ret < 0) {
if ((ENOSPACE(errno) || errno == EACCES) && try)
return 1;
return -1;
}
if (ret == 0) {
mail_storage_set_error(&mbox->storage->storage,
MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT);
return 0;
}
mbox->mbox_dotlocked = TRUE;
if (mbox_file_open_latest(ctx, lock_type) < 0)
return -1;
return 1;
}
static int mbox_lock_dotlock(struct mbox_lock_context *ctx, int lock_type,
time_t max_wait_time ATTR_UNUSED)
{
return mbox_lock_dotlock_int(ctx, lock_type, FALSE);
}
static int mbox_lock_dotlock_try(struct mbox_lock_context *ctx, int lock_type,
time_t max_wait_time ATTR_UNUSED)
{
return mbox_lock_dotlock_int(ctx, lock_type, TRUE);
}
#ifdef HAVE_FLOCK
static int mbox_lock_flock(struct mbox_lock_context *ctx, int lock_type,
time_t max_wait_time)
{
time_t now;
unsigned int next_alarm;
if (mbox_file_open_latest(ctx, lock_type) < 0)
return -1;
if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
return 1;
if (lock_type == F_WRLCK)
lock_type = LOCK_EX;
else if (lock_type == F_RDLCK)
lock_type = LOCK_SH;
else
lock_type = LOCK_UN;
if (max_wait_time == 0) {
/* usually we're waiting here, but if we came from
mbox_lock_dotlock(), we just want to try locking */
lock_type |= LOCK_NB;
} else {
now = time(NULL);
if (now >= max_wait_time)
alarm(1);
else
alarm(I_MIN(max_wait_time - now, 5));
}
while (flock(ctx->mbox->mbox_fd, lock_type) < 0) {
if (errno != EINTR) {
if (errno == EWOULDBLOCK && max_wait_time == 0) {
/* non-blocking lock trying failed */
return 0;
}
alarm(0);
mbox_set_syscall_error(ctx->mbox, "flock()");
return -1;
}
now = time(NULL);
if (now >= max_wait_time) {
alarm(0);
return 0;
}
/* notify locks once every 5 seconds.
try to use rounded values. */
next_alarm = (max_wait_time - now) % 5;
if (next_alarm == 0)
next_alarm = 5;
alarm(next_alarm);
index_storage_lock_notify(&ctx->mbox->box,
MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
max_wait_time - now);
}
alarm(0);
return 1;
}
#endif
#ifdef HAVE_LOCKF
static int mbox_lock_lockf(struct mbox_lock_context *ctx, int lock_type,
time_t max_wait_time)
{
time_t now;
unsigned int next_alarm;
if (mbox_file_open_latest(ctx, lock_type) < 0)
return -1;
if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
return 1;
if (lock_type == F_UNLCK)
lock_type = F_ULOCK;
else if (max_wait_time == 0) {
/* usually we're waiting here, but if we came from
mbox_lock_dotlock(), we just want to try locking */
lock_type = F_TLOCK;
} else {
now = time(NULL);
if (now >= max_wait_time)
alarm(1);
else
alarm(I_MIN(max_wait_time - now, 5));
lock_type = F_LOCK;
}
while (lockf(ctx->mbox->mbox_fd, lock_type, 0) < 0) {
if (errno != EINTR) {
if ((errno == EACCES || errno == EAGAIN) &&
max_wait_time == 0) {
/* non-blocking lock trying failed */
return 0;
}
alarm(0);
mbox_set_syscall_error(ctx->mbox, "lockf()");
return -1;
}
now = time(NULL);
if (now >= max_wait_time) {
alarm(0);
return 0;
}
/* notify locks once every 5 seconds.
try to use rounded values. */
next_alarm = (max_wait_time - now) % 5;
if (next_alarm == 0)
next_alarm = 5;
alarm(next_alarm);
index_storage_lock_notify(&ctx->mbox->box,
MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
max_wait_time - now);
}
alarm(0);
return 1;
}
#endif
static int mbox_lock_fcntl(struct mbox_lock_context *ctx, int lock_type,
time_t max_wait_time)
{
struct flock fl;
time_t now;
unsigned int next_alarm;
int wait_type;
if (mbox_file_open_latest(ctx, lock_type) < 0)
return -1;
if (lock_type == F_UNLCK && ctx->mbox->mbox_fd == -1)
return 1;
memset(&fl, 0, sizeof(fl));
fl.l_type = lock_type;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
if (max_wait_time == 0) {
/* usually we're waiting here, but if we came from
mbox_lock_dotlock(), we just want to try locking */
wait_type = F_SETLK;
} else {
wait_type = F_SETLKW;
now = time(NULL);
if (now >= max_wait_time)
alarm(1);
else
alarm(I_MIN(max_wait_time - now, 5));
}
while (fcntl(ctx->mbox->mbox_fd, wait_type, &fl) < 0) {
if (errno != EINTR) {
if ((errno == EACCES || errno == EAGAIN) &&
wait_type == F_SETLK) {
/* non-blocking lock trying failed */
return 0;
}
alarm(0);
if (errno != EACCES) {
mbox_set_syscall_error(ctx->mbox, "fcntl()");
return -1;
}
mail_storage_set_critical(&ctx->mbox->storage->storage,
"fcntl() failed with mbox file %s: "
"File is locked by another process (EACCES)",
mailbox_get_path(&ctx->mbox->box));
return -1;
}
now = time(NULL);
if (now >= max_wait_time) {
alarm(0);
return 0;
}
/* notify locks once every 5 seconds.
try to use rounded values. */
next_alarm = (max_wait_time - now) % 5;
if (next_alarm == 0)
next_alarm = 5;
alarm(next_alarm);
index_storage_lock_notify(&ctx->mbox->box,
MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
max_wait_time - now);
}
alarm(0);
ctx->fcntl_locked = TRUE;
return 1;
}
static int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
time_t max_wait_time, int idx)
{
enum mbox_lock_type *lock_types;
enum mbox_lock_type type;
int i, ret = 0, lock_status;
ctx->lock_type = lock_type;
lock_types = lock_type == F_WRLCK ||
(lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ?
ctx->mbox->storage->write_locks :
ctx->mbox->storage->read_locks;
for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) {
type = lock_types[i];
lock_status = lock_type != F_UNLCK;
if (ctx->lock_status[type] == lock_status)
continue;
ctx->lock_status[type] = lock_status;
ret = lock_data[type].func(ctx, lock_type, max_wait_time);
if (ret <= 0)
break;
}
return ret;
}
static int mbox_update_locking(struct mbox_mailbox *mbox, int lock_type,
bool *fcntl_locked_r)
{
struct mbox_lock_context ctx;
time_t max_wait_time;
int ret, i;
bool drop_locks;
*fcntl_locked_r = FALSE;
index_storage_lock_notify_reset(&mbox->box);
if (!mbox->storage->lock_settings_initialized)
mbox_init_lock_settings(mbox->storage);
if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) {
/* read-only mbox stream. no need to lock. */
i_assert(mbox_is_backend_readonly(mbox));
mbox->mbox_lock_type = lock_type;
return 1;
}
max_wait_time = time(NULL) +
mail_storage_get_lock_timeout(&mbox->storage->storage,
mbox->storage->set->mbox_lock_timeout);
memset(&ctx, 0, sizeof(ctx));
ctx.mbox = mbox;
if (mbox->mbox_lock_type == F_WRLCK) {
/* dropping to shared lock. first drop those that we
don't remove completely. */
const enum mbox_lock_type *read_locks =
mbox->storage->read_locks;
for (i = 0; i < MBOX_LOCK_COUNT; i++)
ctx.lock_status[i] = 1;
for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
ctx.lock_status[read_locks[i]] = 0;
drop_locks = TRUE;
} else {
drop_locks = FALSE;
}
mbox->mbox_lock_type = lock_type;
ret = mbox_lock_list(&ctx, lock_type, max_wait_time, 0);
if (ret <= 0) {
if (!drop_locks)
(void)mbox_unlock_files(&ctx);
if (ret == 0) {
mail_storage_set_error(&mbox->storage->storage,
MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT);
}
return ret;
}
if (drop_locks) {
/* dropping to shared lock: drop the locks that are only
in write list */
const enum mbox_lock_type *read_locks =
mbox->storage->read_locks;
const enum mbox_lock_type *write_locks =
mbox->storage->write_locks;
memset(ctx.lock_status, 0, sizeof(ctx.lock_status));
for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++)
ctx.lock_status[write_locks[i]] = 1;
for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
ctx.lock_status[read_locks[i]] = 0;
mbox->mbox_lock_type = F_WRLCK;
(void)mbox_lock_list(&ctx, F_UNLCK, 0, 0);
mbox->mbox_lock_type = F_RDLCK;
}
*fcntl_locked_r = ctx.fcntl_locked;
return 1;
}
int mbox_lock(struct mbox_mailbox *mbox, int lock_type,
unsigned int *lock_id_r)
{
const char *path = mailbox_get_path(&mbox->box);
int mbox_fd = mbox->mbox_fd;
bool fcntl_locked;
int ret;
/* allow only unlock -> shared/exclusive or exclusive -> shared */
i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
i_assert(lock_type == F_RDLCK || mbox->mbox_lock_type != F_RDLCK);
/* mbox must be locked before index */
i_assert(mbox->box.index->lock_type != F_WRLCK);
if (mbox->mbox_lock_type == F_UNLCK) {
ret = mbox_update_locking(mbox, lock_type, &fcntl_locked);
if (ret <= 0)
return ret;
if (mbox->storage->storage.set->mail_nfs_storage) {
if (fcntl_locked) {
nfs_flush_attr_cache_fd_locked(path, mbox_fd);
nfs_flush_read_cache_locked(path, mbox_fd);
} else {
nfs_flush_attr_cache_unlocked(path);
nfs_flush_read_cache_unlocked(path, mbox_fd);
}
}
mbox->mbox_lock_id += 2;
}
if (lock_type == F_RDLCK) {
mbox->mbox_shared_locks++;
*lock_id_r = mbox->mbox_lock_id;
} else {
mbox->mbox_excl_locks++;
*lock_id_r = mbox->mbox_lock_id + 1;
}
if (mbox->mbox_stream != NULL)
istream_raw_mbox_set_locked(mbox->mbox_stream);
return 1;
}
static int mbox_unlock_files(struct mbox_lock_context *ctx)
{
int ret = 0;
if (mbox_lock_list(ctx, F_UNLCK, 0, 0) < 0)
ret = -1;
ctx->mbox->mbox_lock_id += 2;
ctx->mbox->mbox_lock_type = F_UNLCK;
return ret;
}
int mbox_unlock(struct mbox_mailbox *mbox, unsigned int lock_id)
{
struct mbox_lock_context ctx;
bool fcntl_locked;
int i;
i_assert(mbox->mbox_lock_id == (lock_id & ~1));
if (lock_id & 1) {
/* dropping exclusive lock */
i_assert(mbox->mbox_excl_locks > 0);
if (--mbox->mbox_excl_locks > 0)
return 0;
if (mbox->mbox_shared_locks > 0) {
/* drop to shared lock */
if (mbox_update_locking(mbox, F_RDLCK,
&fcntl_locked) < 0)
return -1;
return 0;
}
} else {
/* dropping shared lock */
i_assert(mbox->mbox_shared_locks > 0);
if (--mbox->mbox_shared_locks > 0)
return 0;
if (mbox->mbox_excl_locks > 0)
return 0;
}
/* all locks gone */
/* make sure we don't read the stream while unlocked */
if (mbox->mbox_stream != NULL)
istream_raw_mbox_set_unlocked(mbox->mbox_stream);
memset(&ctx, 0, sizeof(ctx));
ctx.mbox = mbox;
for (i = 0; i < MBOX_LOCK_COUNT; i++)
ctx.lock_status[i] = 1;
return mbox_unlock_files(&ctx);
}
void mbox_dotlock_touch(struct mbox_mailbox *mbox)
{
if (mbox->mbox_dotlock == NULL)
return;
if (!mbox->mbox_used_privileges)
(void)file_dotlock_touch(mbox->mbox_dotlock);
else {
(void)mbox_dotlock_privileged_op(mbox, NULL,
MBOX_DOTLOCK_OP_TOUCH);
}
}