bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "lib.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "ioloop.h"
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen#include "array.h"
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen#include "hex-dec.h"
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen#include "hex-binary.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "hostpid.h"
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen#include "istream.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "ostream.h"
4499995f7029bafd85094694b6a14752ea34c9b3Timo Sirainen#include "file-lock.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "file-dotlock.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "mkdir-parents.h"
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen#include "eacces-error.h"
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen#include "str.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "dbox-storage.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include "dbox-file.h"
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen#include <stdio.h>
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include <unistd.h>
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen#include <ctype.h>
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#include <fcntl.h>
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen#define DBOX_READ_BLOCK_SIZE IO_BLOCK_SIZE
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen#ifndef DBOX_FILE_LOCK_METHOD_FLOCK
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainenstatic const struct dotlock_settings dotlock_set = {
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen .stale_timeout = 60*10,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen .use_excl_lock = TRUE
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen};
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#endif
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainenconst char *dbox_generate_tmp_filename(void)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen static unsigned int create_count = 0;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen return t_strdup_printf(DBOX_TEMP_FILE_PREFIX"%"PRIdTIME_T".P%sQ%uM%u.%s",
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ioloop_timeval.tv_sec, my_pid,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen create_count++,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen (unsigned int)ioloop_timeval.tv_usec,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen my_hostname);
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenvoid dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen{
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen mail_storage_set_critical(&file->storage->storage,
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen "%s failed for file %s: %m",
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen function, file->cur_path);
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen}
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainenvoid dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen{
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen va_list args;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen va_start(args, reason);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen mail_storage_set_critical(&file->storage->storage,
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen "Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek file->cur_path, file->input == NULL ? 0 : file->input->v_offset,
a6f281d078ed03d555802c1a8e15fefce80132dcTimo Sirainen t_strdup_vprintf(reason, args));
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen va_end(args);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen file->storage->v.set_file_corrupted(file);
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen}
c1d4780bc0c9017e8e5d366b81e4fad31174c0adTimo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenvoid dbox_file_init(struct dbox_file *file)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen file->refcount = 1;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen file->fd = -1;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen file->cur_offset = (uoff_t)-1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen file->cur_path = file->primary_path;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenvoid dbox_file_free(struct dbox_file *file)
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_assert(file->refcount == 0);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen pool_unref(&file->metadata_pool);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_close(file);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_free(file->primary_path);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_free(file->alt_path);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_free(file);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenvoid dbox_file_unref(struct dbox_file **_file)
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen{
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen struct dbox_file *file = *_file;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen *_file = NULL;
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen i_assert(file->refcount > 0);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (--file->refcount == 0)
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen file->storage->v.file_unrefed(file);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainenstatic int dbox_file_parse_header(struct dbox_file *file, const char *line)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen{
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen const char *const *tmp, *value;
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen unsigned int pos;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen enum dbox_header_key key;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen file->file_version = *line - '0';
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (!i_isdigit(line[0]) || line[1] != ' ' ||
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen (file->file_version != 1 && file->file_version != DBOX_VERSION)) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_corrupted(file, "Invalid dbox version");
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen return -1;
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen }
ac6bba612af5207c24f6f02497d64b0ea03e7bbdTimo Sirainen line += 2;
ac6bba612af5207c24f6f02497d64b0ea03e7bbdTimo Sirainen pos = 2;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen file->msg_header_size = 0;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) {
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen uintmax_t time;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen key = **tmp;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen value = *tmp + 1;
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen switch (key) {
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen case DBOX_HEADER_OLDV1_APPEND_OFFSET:
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen break;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen case DBOX_HEADER_MSG_HEADER_SIZE:
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen if (str_to_uint_hex(value, &file->msg_header_size) < 0) {
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen dbox_file_set_corrupted(file, "Invalid message header size");
7f74811b78f8915e73dffc88bb49009e98b6846dTimo Sirainen return -1;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen }
3fe5eaeb32a8bbc31ce0673793b1c37f72d00d47Timo Sirainen break;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen case DBOX_HEADER_CREATE_STAMP:
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen if (str_to_uintmax_hex(value, &time) < 0) {
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen dbox_file_set_corrupted(file, "Invalid create time stamp");
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen return -1;
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen }
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen file->create_time = (time_t)time;
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen break;
159de4e2ce3f541a9aa745d158b31203a40711e1Timo Sirainen }
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen pos += strlen(value) + 2;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (file->msg_header_size == 0) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_corrupted(file, "Missing message header size");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen return 0;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic int dbox_file_read_header(struct dbox_file *file)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen const char *line;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen unsigned int hdr_size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen int ret;
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen
8d56f3334e22619abf56833d290bb1f49ac6722cTimo Sirainen i_stream_seek(file->input, 0);
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen line = i_stream_read_next_line(file->input);
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen if (line == NULL) {
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen if (file->input->stream_errno == 0) {
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen dbox_file_set_corrupted(file,
b8c009e1639efa53eb9e70720f28ce137d493e5eTimo Sirainen "EOF while reading file header");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return 0;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
156736910057b280cb9999d4c6c7221c4c80f5c2Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_syscall_error(file, "read()");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen hdr_size = file->input->v_offset;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen T_BEGIN {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1;
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen } T_END;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (ret > 0)
81b1d14891415fef0c2f37ef1ef3680cdcc600f1Timo Sirainen file->file_header_size = hdr_size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainenstatic int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen{
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen const char *path;
5069adb2f5b3609fff9a0a705c6edeae56e0030aTimo Sirainen int flags = O_RDWR;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen bool alt = FALSE;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* try the primary path first */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen path = file->primary_path;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen while ((file->fd = open(path, flags)) == -1) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (errno == EACCES && flags == O_RDWR) {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen flags = O_RDONLY;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen continue;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (errno != ENOENT) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen mail_storage_set_critical(&file->storage->storage,
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen "open(%s) failed: %m", path);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return -1;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (file->alt_path == NULL || alt || !try_altpath) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen /* not found */
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return 0;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen /* try the alternative path */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen path = file->alt_path;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen alt = TRUE;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen }
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen file->cur_path = path;
8d56f3334e22619abf56833d290bb1f49ac6722cTimo Sirainen return 1;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen}
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenstatic int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen bool *notfound_r)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
5297aa3ceddf3a4ecc09f49c832bc424eff8f906Timo Sirainen int ret, fd;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen *notfound_r = FALSE;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (file->input != NULL)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return 1;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (file->fd == -1) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen T_BEGIN {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen ret = dbox_file_open_fd(file, try_altpath);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen } T_END;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (ret <= 0) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (ret < 0)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return -1;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen *notfound_r = TRUE;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return 1;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* we're manually checking at dbox_file_close() if we need to close the
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen fd or not. */
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen fd = file->fd;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen file->input = i_stream_create_fd_autoclose(&fd, DBOX_READ_BLOCK_SIZE);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_set_name(file->input, file->cur_path);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return dbox_file_read_header(file);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen}
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainenint dbox_file_open(struct dbox_file *file, bool *deleted_r)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen{
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return dbox_file_open_full(file, TRUE, deleted_r);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen}
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainenint dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen{
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return dbox_file_open_full(file, FALSE, notfound_r);
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen}
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainenint dbox_file_stat(struct dbox_file *file, struct stat *st_r)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen{
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen const char *path;
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen bool alt = FALSE;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (dbox_file_is_open(file)) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (fstat(file->fd, st_r) < 0) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen mail_storage_set_critical(&file->storage->storage,
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen "fstat(%s) failed: %m", file->cur_path);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return 0;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* try the primary path first */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen path = file->primary_path;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen while (stat(path, st_r) < 0) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (errno != ENOENT) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen mail_storage_set_critical(&file->storage->storage,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen "stat(%s) failed: %m", path);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (file->alt_path == NULL || alt) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* not found */
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen return -1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* try the alternative path */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen path = file->alt_path;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen alt = TRUE;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen file->cur_path = path;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return 0;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen}
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenint dbox_file_header_write(struct dbox_file *file, struct ostream *output)
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen{
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen string_t *hdr;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen hdr = t_str_new(128);
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION,
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen DBOX_HEADER_MSG_HEADER_SIZE,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen (unsigned int)sizeof(struct dbox_message_header),
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen file->file_version = DBOX_VERSION;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen file->file_header_size = str_len(hdr);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen file->msg_header_size = sizeof(struct dbox_message_header);
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen return o_stream_send(output, str_data(hdr), str_len(hdr));
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen}
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainenvoid dbox_file_close(struct dbox_file *file)
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen{
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen dbox_file_unlock(file);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen if (file->input != NULL) {
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen /* stream autocloses the fd when it gets destroyed. note that
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen the stream may outlive the struct dbox_file. */
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen i_stream_unref(&file->input);
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen file->fd = -1;
34830cefe1757de0ffca67acdc529d5bc8b06b66Timo Sirainen } else if (file->fd != -1) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (close(file->fd) < 0)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_syscall_error(file, "close()");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen file->fd = -1;
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen file->cur_offset = (uoff_t)-1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainenint dbox_file_try_lock(struct dbox_file *file)
1b9aae1cb53708b5a3d861b4db6ba96ac2eb35b4Timo Sirainen{
1b9aae1cb53708b5a3d861b4db6ba96ac2eb35b4Timo Sirainen int ret;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen i_assert(file->fd != -1);
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen#ifdef DBOX_FILE_LOCK_METHOD_FLOCK
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen FILE_LOCK_METHOD_FLOCK, &file->lock);
4da70fe8c9cb6e57b36103d78ab1e9c8654f76d9Timo Sirainen if (ret < 0) {
7efee0bb408b0d5253e41997857bdda57855cdc7Timo Sirainen mail_storage_set_critical(&file->storage->storage,
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen "file_try_lock(%s) failed: %m", file->cur_path);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#else
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen ret = file_dotlock_create(&dotlock_set, file->cur_path,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen DOTLOCK_CREATE_FLAG_NONBLOCK, &file->lock);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (ret < 0) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen mail_storage_set_critical(&file->storage->storage,
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen "file_dotlock_create(%s) failed: %m", file->cur_path);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen#endif
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return ret;
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen}
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainenvoid dbox_file_unlock(struct dbox_file *file)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen{
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen i_assert(!file->appending || file->lock == NULL);
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen if (file->lock != NULL) {
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen#ifdef DBOX_FILE_LOCK_METHOD_FLOCK
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen file_unlock(&file->lock);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen#else
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen file_dotlock_delete(&file->lock);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen#endif
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen }
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen if (file->input != NULL)
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen i_stream_sync(file->input);
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen}
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainenint dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen struct dbox_message_header hdr;
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen const unsigned char *data;
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen size_t size;
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen int ret;
31fc5ca967261b131646ff9719eb1fb23888ed1aTimo Sirainen
2a8b891366a3fc69524c2bb07f68d42c16223a56Timo Sirainen ret = i_stream_read_bytes(file->input, &data, &size,
2a8b891366a3fc69524c2bb07f68d42c16223a56Timo Sirainen file->msg_header_size);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen if (ret <= 0) {
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen if (file->input->stream_errno == 0) {
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen /* EOF, broken offset or file truncated */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen dbox_file_set_corrupted(file, "EOF reading msg header "
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen "(got %"PRIuSIZE_T"/%u bytes)",
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen size, file->msg_header_size);
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen return 0;
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen dbox_file_set_syscall_error(file, "read()");
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return -1;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
8255a22cccf3b0ccf38206c594941820ac1c9e00Timo Sirainen memcpy(&hdr, data, I_MIN(sizeof(hdr), file->msg_header_size));
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* probably broken offset */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen dbox_file_set_corrupted(file, "msg header has bad magic value");
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen return 0;
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen }
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen if (data[file->msg_header_size-1] != '\n') {
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen dbox_file_set_corrupted(file, "msg header doesn't end with LF");
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen return 0;
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen }
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen *physical_size_r = hex2dec(hdr.message_size_hex,
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen sizeof(hdr.message_size_hex));
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen return 1;
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen}
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainenint dbox_file_seek(struct dbox_file *file, uoff_t offset)
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen{
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen uoff_t size;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen int ret;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen i_assert(file->input != NULL);
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen if (offset == 0)
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen offset = file->file_header_size;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen if (offset != file->cur_offset) {
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen i_stream_seek(file->input, offset);
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen ret = dbox_file_read_mail_header(file, &size);
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen if (ret <= 0)
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen return ret;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen file->cur_offset = offset;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen file->cur_physical_size = size;
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen }
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen i_stream_seek(file->input, offset + file->msg_header_size);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen return 1;
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen}
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainenstatic int
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainendbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen const char *line;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen size_t buf_size;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen int ret;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen i_stream_seek(file->input, *offset);
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen return ret;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen /* skip over the actual metadata */
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen buf_size = i_stream_get_max_buffer_size(file->input);
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen i_stream_set_max_buffer_size(file->input, (size_t)-1);
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen while ((line = i_stream_read_next_line(file->input)) != NULL) {
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* end of metadata */
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen break;
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen }
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen }
fcca16701767c6b92227a9ee125de69d257882f6Timo Sirainen i_stream_set_max_buffer_size(file->input, buf_size);
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen *offset = file->input->v_offset;
7bcb308d0e13dfa48b483b0addccd889a77bb598Timo Sirainen return 1;
d10cb4d7a80571af21f776c65604442bf09b1765Timo Sirainen}
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenvoid dbox_file_seek_rewind(struct dbox_file *file)
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen{
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen file->cur_offset = (uoff_t)-1;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen}
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenint dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen{
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen uoff_t offset;
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen int ret;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen i_assert(file->input != NULL);
be51dfea768ad502e08ebd02917138f7a0f8f625Timo Sirainen
448ab3576e0def2135c6f4681bd412dd59425d44Timo Sirainen if (file->cur_offset == (uoff_t)-1) {
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen /* first mail. we may not have read the file at all yet,
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen so set the offset afterwards. */
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen offset = 0;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen } else {
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen offset = file->cur_offset + file->msg_header_size +
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen file->cur_physical_size;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen *offset_r = file->cur_offset;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return ret;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen if (i_stream_read_eof(file->input)) {
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen *last_r = TRUE;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen return 0;
3e55775bc7a37ebc05e06c04cafb32eee9888e87Timo Sirainen }
2201e2cc1b3f744dac61c2bf8095bcb6b5719540Timo Sirainen }
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen *offset_r = offset;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen *last_r = FALSE;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen ret = dbox_file_seek(file, offset);
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen if (*offset_r == 0)
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen *offset_r = file->file_header_size;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen return ret;
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen}
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainenstruct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen{
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen struct dbox_file_append_context *ctx;
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen i_assert(!file->appending);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen file->appending = TRUE;
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen ctx = i_new(struct dbox_file_append_context, 1);
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen ctx->file = file;
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen if (file->fd != -1) {
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen o_stream_set_name(ctx->output, file->cur_path);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen o_stream_set_finish_via_child(ctx->output, FALSE);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen o_stream_cork(ctx->output);
4d33a3133e8484ebd00f677f457cda82f1365b84Timo Sirainen }
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen return ctx;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen}
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainenint dbox_file_append_commit(struct dbox_file_append_context **_ctx)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen{
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen struct dbox_file_append_context *ctx = *_ctx;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen int ret;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen i_assert(ctx->file->appending);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen *_ctx = NULL;
9e406b04bb5bed7d73aeed375c40c6a3fea1a2cbTimo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen ret = dbox_file_append_flush(ctx);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (ctx->last_checkpoint_offset != ctx->output->offset) {
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen o_stream_close(ctx->output);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) {
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen dbox_file_set_syscall_error(ctx->file, "ftruncate()");
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen return -1;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen }
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen }
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen o_stream_unref(&ctx->output);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen ctx->file->appending = FALSE;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen i_free(ctx);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen return ret;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen}
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainenvoid dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen{
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen struct dbox_file_append_context *ctx = *_ctx;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen struct dbox_file *file = ctx->file;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen bool close_file = FALSE;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen i_assert(ctx->file->appending);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen
17466c748183b19f7775374ce6afbd7bf62d479dTimo Sirainen *_ctx = NULL;
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen if (ctx->first_append_offset == 0) {
d907b51d224e965a876eada6bc49455773e416e9Timo Sirainen /* nothing changed */
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen } else if (ctx->first_append_offset == file->file_header_size) {
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen /* rolling back everything */
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen if (unlink(file->cur_path) < 0)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen dbox_file_set_syscall_error(file, "unlink()");
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen close_file = TRUE;
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen } else {
c395e7d730eb4ee17e2b619acec487637a785110Timo Sirainen /* truncating only some mails */
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen o_stream_close(ctx->output);
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen if (ftruncate(file->fd, ctx->first_append_offset) < 0)
6967fa47dde9f2726bd86019a50627dacf2d7509Timo Sirainen dbox_file_set_syscall_error(file, "ftruncate()");
}
if (ctx->output != NULL) {
o_stream_abort(ctx->output);
o_stream_unref(&ctx->output);
}
i_free(ctx);
if (close_file)
dbox_file_close(file);
file->appending = FALSE;
}
int dbox_file_append_flush(struct dbox_file_append_context *ctx)
{
struct mail_storage *storage = &ctx->file->storage->storage;
if (ctx->last_flush_offset == ctx->output->offset &&
ctx->last_checkpoint_offset == ctx->output->offset)
return 0;
if (o_stream_flush(ctx->output) < 0) {
dbox_file_set_syscall_error(ctx->file, "write()");
return -1;
}
if (ctx->last_checkpoint_offset != ctx->output->offset) {
if (ftruncate(ctx->file->fd, ctx->last_checkpoint_offset) < 0) {
dbox_file_set_syscall_error(ctx->file, "ftruncate()");
return -1;
}
if (o_stream_seek(ctx->output, ctx->last_checkpoint_offset) < 0) {
dbox_file_set_syscall_error(ctx->file, "lseek()");
return -1;
}
}
if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
if (fdatasync(ctx->file->fd) < 0) {
dbox_file_set_syscall_error(ctx->file, "fdatasync()");
return -1;
}
}
ctx->last_flush_offset = ctx->output->offset;
return 0;
}
void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx)
{
ctx->last_checkpoint_offset = ctx->output->offset;
}
int dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
struct ostream **output_r)
{
struct dbox_file *file = ctx->file;
struct stat st;
if (ctx->output == NULL) {
/* file creation had failed */
return -1;
}
if (ctx->last_checkpoint_offset != ctx->output->offset) {
/* a message was aborted. don't try appending to this
file anymore. */
return -1;
}
if (file->file_version == 0) {
/* newly created file, write the file header */
if (dbox_file_header_write(file, ctx->output) < 0) {
dbox_file_set_syscall_error(file, "write()");
return -1;
}
*output_r = ctx->output;
return 1;
}
/* file has existing mails */
if (file->file_version != DBOX_VERSION ||
file->msg_header_size != sizeof(struct dbox_message_header)) {
/* created by an incompatible version, can't append */
return 0;
}
if (ctx->output->offset == 0) {
/* first append to existing file. seek to eof first. */
if (fstat(file->fd, &st) < 0) {
dbox_file_set_syscall_error(file, "fstat()");
return -1;
}
if (st.st_size < file->msg_header_size) {
dbox_file_set_corrupted(file,
"dbox file size too small");
return 0;
}
if (o_stream_seek(ctx->output, st.st_size) < 0) {
dbox_file_set_syscall_error(file, "lseek()");
return -1;
}
}
*output_r = ctx->output;
return 1;
}
int dbox_file_metadata_skip_header(struct dbox_file *file)
{
struct dbox_metadata_header metadata_hdr;
const unsigned char *data;
size_t size;
int ret;
ret = i_stream_read_bytes(file->input, &data, &size,
sizeof(metadata_hdr));
if (ret <= 0) {
if (file->input->stream_errno == 0) {
/* EOF, broken offset */
dbox_file_set_corrupted(file,
"Unexpected EOF while reading metadata header");
return 0;
}
dbox_file_set_syscall_error(file, "read()");
return -1;
}
memcpy(&metadata_hdr, data, sizeof(metadata_hdr));
if (memcmp(metadata_hdr.magic_post, DBOX_MAGIC_POST,
sizeof(metadata_hdr.magic_post)) != 0) {
/* probably broken offset */
dbox_file_set_corrupted(file,
"metadata header has bad magic value");
return 0;
}
i_stream_skip(file->input, sizeof(metadata_hdr));
return 1;
}
static int
dbox_file_metadata_read_at(struct dbox_file *file, uoff_t metadata_offset)
{
const char *line;
size_t buf_size;
int ret;
if (file->metadata_pool != NULL)
p_clear(file->metadata_pool);
else {
file->metadata_pool =
pool_alloconly_create("dbox metadata", 1024);
}
p_array_init(&file->metadata, file->metadata_pool, 16);
i_stream_seek(file->input, metadata_offset);
if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
return ret;
ret = 0;
buf_size = i_stream_get_max_buffer_size(file->input);
/* use unlimited line length for metadata */
i_stream_set_max_buffer_size(file->input, (size_t)-1);
while ((line = i_stream_read_next_line(file->input)) != NULL) {
if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
/* end of metadata */
ret = 1;
break;
}
line = p_strdup(file->metadata_pool, line);
array_append(&file->metadata, &line, 1);
}
i_stream_set_max_buffer_size(file->input, buf_size);
if (ret == 0)
dbox_file_set_corrupted(file, "missing end-of-metadata line");
return ret;
}
int dbox_file_metadata_read(struct dbox_file *file)
{
uoff_t metadata_offset;
int ret;
i_assert(file->cur_offset != (uoff_t)-1);
if (file->metadata_read_offset == file->cur_offset)
return 1;
metadata_offset = file->cur_offset + file->msg_header_size +
file->cur_physical_size;
ret = dbox_file_metadata_read_at(file, metadata_offset);
if (ret <= 0)
return ret;
file->metadata_read_offset = file->cur_offset;
return 1;
}
const char *dbox_file_metadata_get(struct dbox_file *file,
enum dbox_metadata_key key)
{
const char *const *metadata;
unsigned int i, count;
metadata = array_get(&file->metadata, &count);
for (i = 0; i < count; i++) {
if (*metadata[i] == (char)key)
return metadata[i] + 1;
}
return NULL;
}
uoff_t dbox_file_get_plaintext_size(struct dbox_file *file)
{
const char *value;
uintmax_t size;
i_assert(file->metadata_read_offset == file->cur_offset);
/* see if we have it in metadata */
value = dbox_file_metadata_get(file, DBOX_METADATA_PHYSICAL_SIZE);
if (value == NULL ||
str_to_uintmax_hex(value, &size) < 0 ||
size > (uoff_t)-1) {
/* no. that means we can use the size in the header */
return file->cur_physical_size;
}
return (uoff_t)size;
}
void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr,
uoff_t message_size)
{
memset(dbox_msg_hdr, ' ', sizeof(*dbox_msg_hdr));
memcpy(dbox_msg_hdr->magic_pre, DBOX_MAGIC_PRE,
sizeof(dbox_msg_hdr->magic_pre));
dbox_msg_hdr->type = DBOX_MESSAGE_TYPE_NORMAL;
dec2hex(dbox_msg_hdr->message_size_hex, message_size,
sizeof(dbox_msg_hdr->message_size_hex));
dbox_msg_hdr->save_lf = '\n';
}
int dbox_file_unlink(struct dbox_file *file)
{
const char *path;
bool alt = FALSE;
path = file->primary_path;
while (unlink(path) < 0) {
if (errno != ENOENT) {
mail_storage_set_critical(&file->storage->storage,
"unlink(%s) failed: %m", path);
return -1;
}
if (file->alt_path == NULL || alt) {
/* not found */
return 0;
}
/* try the alternative path */
path = file->alt_path;
alt = TRUE;
}
return 1;
}