bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "lib.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "istream.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "mbox-storage.h"
3343a61404603b21c246783a7963b77833095f31Timo Sirainen#include "mbox-sync-private.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "mbox-file.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "istream-raw-mbox.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include <sys/stat.h>
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen#include <utime.h>
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen#define MBOX_READ_BLOCK_SIZE IO_BLOCK_SIZE
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenint mbox_file_open(struct mbox_mailbox *mbox)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen struct stat st;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen int fd;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen i_assert(mbox->mbox_fd == -1);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox->mbox_file_stream != NULL) {
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen /* read-only mbox stream */
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen i_assert(mbox_is_backend_readonly(mbox));
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen return 0;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen }
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen fd = open(mailbox_get_path(&mbox->box),
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen mbox_is_backend_readonly(mbox) ? O_RDONLY : O_RDWR);
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen if (fd == -1 && errno == EACCES && !mbox->backend_readonly) {
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen mbox->backend_readonly = TRUE;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen fd = open(mailbox_get_path(&mbox->box), O_RDONLY);
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainen }
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (fd == -1) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox_set_syscall_error(mbox, "open()");
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return -1;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (fstat(fd, &st) < 0) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox_set_syscall_error(mbox, "fstat()");
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&fd);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return -1;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->mbox_writeonly = S_ISFIFO(st.st_mode);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->mbox_fd = fd;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->mbox_dev = st.st_dev;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->mbox_ino = st.st_ino;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return 0;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenvoid mbox_file_close(struct mbox_mailbox *mbox)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox_file_close_stream(mbox);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox->mbox_fd != -1) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (close(mbox->mbox_fd) < 0)
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen mbox_set_syscall_error(mbox, "close()");
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->mbox_fd = -1;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenint mbox_file_open_stream(struct mbox_mailbox *mbox)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox->mbox_stream != NULL)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return 0;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox->mbox_file_stream != NULL) {
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen /* read-only mbox stream */
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen i_assert(mbox->mbox_fd == -1 && mbox_is_backend_readonly(mbox));
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen } else {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen if (mbox->mbox_fd == -1) {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen if (mbox_file_open(mbox) < 0)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen return -1;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen }
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen if (mbox->mbox_writeonly) {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen mbox->mbox_file_stream =
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen i_stream_create_from_data("", 0);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen } else {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen mbox->mbox_file_stream =
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen i_stream_create_fd(mbox->mbox_fd,
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi MBOX_READ_BLOCK_SIZE);
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen i_stream_set_init_buffer_size(mbox->mbox_file_stream,
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen MBOX_READ_BLOCK_SIZE);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen }
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen i_stream_set_name(mbox->mbox_file_stream,
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen mailbox_get_path(&mbox->box));
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen mbox->mbox_stream = i_stream_create_raw_mbox(mbox->mbox_file_stream);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen if (mbox->mbox_lock_type != F_UNLCK)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen istream_raw_mbox_set_locked(mbox->mbox_stream);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return 0;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainenstatic void mbox_file_fix_atime(struct mbox_mailbox *mbox)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen{
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen struct utimbuf buf;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen struct stat st;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen if (mbox->box.recent_flags_count > 0 &&
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 &&
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen mbox->mbox_fd != -1 && !mbox_is_backend_readonly(mbox)) {
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen /* we've seen recent messages which we want to keep recent.
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen keep file's atime lower than mtime so \Marked status
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen gets shown while listing */
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (fstat(mbox->mbox_fd, &st) < 0) {
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen mbox_set_syscall_error(mbox, "fstat()");
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen return;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen }
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (st.st_atime >= st.st_mtime) {
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen buf.modtime = st.st_mtime;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen buf.actime = buf.modtime - 1;
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen /* EPERM can happen with shared mailboxes */
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (utime(mailbox_get_path(&mbox->box), &buf) < 0 &&
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen errno != EPERM)
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen mbox_set_syscall_error(mbox, "utime()");
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen }
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen }
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen}
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenvoid mbox_file_close_stream(struct mbox_mailbox *mbox)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen /* if we read anything, fix the atime if needed */
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen mbox_file_fix_atime(mbox);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
be5773cb4d6edae8a5d9f300c3c7375cdd33826eJosef 'Jeff' Sipek i_stream_destroy(&mbox->mbox_stream);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox->mbox_file_stream != NULL) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox->mbox_fd == -1) {
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen /* read-only mbox stream */
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen i_assert(mbox_is_backend_readonly(mbox));
424236b2b88a5a7bbde5cf6a6b32189ca3437629Timo Sirainen i_stream_seek(mbox->mbox_file_stream, 0);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen } else {
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen i_stream_destroy(&mbox->mbox_file_stream);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen }
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainenint mbox_file_lookup_offset(struct mbox_mailbox *mbox,
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen struct mail_index_view *view,
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen uint32_t seq, uoff_t *offset_r)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen{
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen const void *data;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen bool deleted;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen mail_index_lookup_ext(view, seq, mbox->mbox_ext_idx, &data, &deleted);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen if (deleted)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return -1;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen if (data == NULL) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&mbox->box,
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi "Cached message offset lost for seq %u in mbox", seq);
761c441db58493fcf10d3418b0cabadc3028cfb6Timo Sirainen mbox->mbox_hdr.dirty_flag = 1;
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen mbox->mbox_broken_offsets = TRUE;
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen return 0;
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen }
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen *offset_r = *((const uint64_t *)data);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen return 1;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen}
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainenint mbox_file_seek(struct mbox_mailbox *mbox, struct mail_index_view *view,
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen uint32_t seq, bool *deleted_r)
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen{
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen uoff_t offset;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen int ret;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen ret = mbox_file_lookup_offset(mbox, view, seq, &offset);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen if (ret <= 0) {
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen *deleted_r = ret < 0;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen return ret;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen }
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen *deleted_r = FALSE;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (istream_raw_mbox_seek(mbox->mbox_stream, offset) < 0) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (offset == 0) {
e5afebd2df1d4990f7bec2a839260ff2e6d78168Timo Sirainen mbox->invalid_mbox_file = TRUE;
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen mail_storage_set_error(&mbox->storage->storage,
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen MAIL_ERROR_NOTPOSSIBLE,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen "Mailbox isn't a valid mbox file");
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return -1;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if (mbox->mbox_hdr.dirty_flag != 0)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return 0;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&mbox->box,
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi "Cached message offset %s is invalid for mbox",
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi dec2str(offset));
761c441db58493fcf10d3418b0cabadc3028cfb6Timo Sirainen mbox->mbox_hdr.dirty_flag = 1;
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen mbox->mbox_broken_offsets = TRUE;
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen return 0;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen if (mbox->mbox_hdr.dirty_flag != 0) {
3343a61404603b21c246783a7963b77833095f31Timo Sirainen /* we're dirty - make sure this is the correct mail */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (!mbox_sync_parse_match_mail(mbox, view, seq))
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return 0;
3343a61404603b21c246783a7963b77833095f31Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ret = istream_raw_mbox_seek(mbox->mbox_stream, offset);
3343a61404603b21c246783a7963b77833095f31Timo Sirainen i_assert(ret == 0);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return 1;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}