mbox-file.c revision 3d4c24127f4f83259c0f81851184abc34793dbe0
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen#include "lib.h"
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen#include "istream.h"
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen#include "mbox-storage.h"
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen#include "mbox-sync-private.h"
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen#include "mbox-file.h"
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen#include "istream-raw-mbox.h"
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen#include <sys/stat.h>
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen#include <utime.h>
c37098f8ce6d512ba41f09564d04ed25720f0a77Timo Sirainen
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen#define MBOX_READ_BLOCK_SIZE IO_BLOCK_SIZE
2e5d624013c30633e8ded148d338ce46c321a995Timo Sirainen
88b9f9eb91da632d3e941fe4276f8ace03205b25Timo Sirainenint mbox_file_open(struct mbox_mailbox *mbox)
f26ef7a3a562dc42a1e9a4dde546bd30df3241e8Timo Sirainen{
57b523eeb99ed5d7f5002907a409cdef54353ce5Timo Sirainen struct stat st;
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen int fd;
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen i_assert(mbox->mbox_fd == -1);
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen if (mbox->mbox_file_stream != NULL) {
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen /* read-only mbox stream */
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen i_assert(mbox->box.backend_readonly);
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen return 0;
c8296ac1ed68ed5c5168de545b76f9b27fc76d35Timo Sirainen }
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen
5b6470e0e2ef4012430cdeca7d9b89c1278a0ed4Timo Sirainen fd = open(mbox->box.path,
5b6470e0e2ef4012430cdeca7d9b89c1278a0ed4Timo Sirainen mbox->box.backend_readonly ? O_RDONLY : O_RDWR);
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen if (fd == -1 && errno == EACCES && !mbox->box.backend_readonly) {
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen mbox->box.backend_readonly = TRUE;
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen fd = open(mbox->box.path, O_RDONLY);
57f4445a46726a17bfe78b0964dd301a6ccb40ecTimo Sirainen }
if (fd == -1) {
mbox_set_syscall_error(mbox, "open()");
return -1;
}
if (fstat(fd, &st) < 0) {
mbox_set_syscall_error(mbox, "fstat()");
(void)close(fd);
return -1;
}
mbox->mbox_writeonly = S_ISFIFO(st.st_mode);
mbox->mbox_fd = fd;
mbox->mbox_dev = st.st_dev;
mbox->mbox_ino = st.st_ino;
return 0;
}
void mbox_file_close(struct mbox_mailbox *mbox)
{
mbox_file_close_stream(mbox);
if (mbox->mbox_fd != -1) {
if (close(mbox->mbox_fd) < 0)
mbox_set_syscall_error(mbox, "close()");
mbox->mbox_fd = -1;
}
}
int mbox_file_open_stream(struct mbox_mailbox *mbox)
{
if (mbox->mbox_stream != NULL)
return 0;
if (mbox->mbox_file_stream != NULL) {
/* read-only mbox stream */
i_assert(mbox->mbox_fd == -1 && mbox->box.backend_readonly);
} else {
if (mbox->mbox_fd == -1) {
if (mbox_file_open(mbox) < 0)
return -1;
}
if (mbox->mbox_writeonly) {
mbox->mbox_file_stream =
i_stream_create_from_data(NULL, 0);
} else {
mbox->mbox_file_stream =
i_stream_create_fd(mbox->mbox_fd,
MBOX_READ_BLOCK_SIZE,
FALSE);
i_stream_set_init_buffer_size(mbox->mbox_file_stream,
MBOX_READ_BLOCK_SIZE);
}
i_stream_set_name(mbox->mbox_file_stream, mbox->box.path);
}
mbox->mbox_stream = i_stream_create_raw_mbox(mbox->mbox_file_stream);
if (mbox->mbox_lock_type != F_UNLCK)
istream_raw_mbox_set_locked(mbox->mbox_stream);
return 0;
}
static void mbox_file_fix_atime(struct mbox_mailbox *mbox)
{
struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(&mbox->box);
struct utimbuf buf;
struct stat st;
if (ibox->recent_flags_count > 0 &&
(mbox->box.flags & MAILBOX_FLAG_KEEP_RECENT) != 0 &&
mbox->mbox_fd != -1 && !mbox->box.backend_readonly) {
/* we've seen recent messages which we want to keep recent.
keep file's atime lower than mtime so \Marked status
gets shown while listing */
if (fstat(mbox->mbox_fd, &st) < 0) {
mbox_set_syscall_error(mbox, "fstat()");
return;
}
if (st.st_atime >= st.st_mtime) {
buf.modtime = st.st_mtime;
buf.actime = buf.modtime - 1;
/* EPERM can happen with shared mailboxes */
if (utime(mbox->box.path, &buf) < 0 && errno != EPERM)
mbox_set_syscall_error(mbox, "utime()");
}
}
}
void mbox_file_close_stream(struct mbox_mailbox *mbox)
{
/* if we read anything, fix the atime if needed */
mbox_file_fix_atime(mbox);
if (mbox->mbox_stream != NULL)
i_stream_destroy(&mbox->mbox_stream);
if (mbox->mbox_file_stream != NULL) {
if (mbox->mbox_fd == -1) {
/* read-only mbox stream */
i_assert(mbox->box.backend_readonly);
i_stream_seek(mbox->mbox_file_stream, 0);
} else {
i_stream_destroy(&mbox->mbox_file_stream);
}
}
}
int mbox_file_lookup_offset(struct mbox_mailbox *mbox,
struct mail_index_view *view,
uint32_t seq, uoff_t *offset_r)
{
const void *data;
bool deleted;
mail_index_lookup_ext(view, seq, mbox->mbox_ext_idx, &data, &deleted);
if (deleted)
return -1;
if (data == NULL) {
mail_storage_set_critical(&mbox->storage->storage,
"Cached message offset lost for seq %u in mbox file %s",
seq, mbox->box.path);
mbox->mbox_hdr.dirty_flag = TRUE;
mbox->mbox_broken_offsets = TRUE;
return 0;
}
*offset_r = *((const uint64_t *)data);
return 1;
}
int mbox_file_seek(struct mbox_mailbox *mbox, struct mail_index_view *view,
uint32_t seq, bool *deleted_r)
{
uoff_t offset;
int ret;
ret = mbox_file_lookup_offset(mbox, view, seq, &offset);
if (ret <= 0) {
*deleted_r = ret < 0;
return ret;
}
*deleted_r = FALSE;
if (istream_raw_mbox_seek(mbox->mbox_stream, offset) < 0) {
if (offset == 0) {
mbox->invalid_mbox_file = TRUE;
mail_storage_set_error(&mbox->storage->storage,
MAIL_ERROR_NOTPOSSIBLE,
"Mailbox isn't a valid mbox file");
return -1;
}
if (mbox->mbox_hdr.dirty_flag)
return 0;
mail_storage_set_critical(&mbox->storage->storage,
"Cached message offset %s is invalid for mbox file %s",
dec2str(offset), mbox->box.path);
mbox->mbox_hdr.dirty_flag = TRUE;
mbox->mbox_broken_offsets = TRUE;
return 0;
}
if (mbox->mbox_hdr.dirty_flag) {
/* we're dirty - make sure this is the correct mail */
if (!mbox_sync_parse_match_mail(mbox, view, seq))
return 0;
ret = istream_raw_mbox_seek(mbox->mbox_stream, offset);
i_assert(ret == 0);
}
return 1;
}