dbox-file.c revision ded851166b85350bbfbe8bcf28996c1fc1c9e8be
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "lib.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "ioloop.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "array.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "hex-dec.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "hex-binary.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "hostpid.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "istream.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "ostream.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "file-lock.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mkdir-parents.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "fdatasync-path.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "eacces-error.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "str.h"
25d624dd86700c82cd28427f3d3bebe7c8f7f459Timo Sirainen#include "dbox-storage.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "dbox-file.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <stdio.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <stdlib.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <unistd.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <ctype.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <fcntl.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define DBOX_READ_BLOCK_SIZE 4096
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainenconst char *dbox_generate_tmp_filename(void)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen static unsigned int create_count = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return t_strdup_printf("temp.%lu.P%sQ%uM%u.%s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (unsigned long)ioloop_timeval.tv_sec, my_pid,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen create_count++,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (unsigned int)ioloop_timeval.tv_usec,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen my_hostname);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(&file->storage->storage,
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen "%s failed for file %s: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen function, file->cur_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen va_list args;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
15a07b47846c47a81d69a14d649564e222d6f742Timo Sirainen file->storage->files_corrupted = TRUE;
15a07b47846c47a81d69a14d649564e222d6f742Timo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen va_start(args, reason);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen mail_storage_set_critical(&file->storage->storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->cur_path, file->input == NULL ? 0 : file->input->v_offset,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen t_strdup_vprintf(reason, args));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen va_end(args);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid dbox_file_init(struct dbox_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->refcount = 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->fd = -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->cur_offset = (uoff_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->cur_path = file->primary_path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
63110c906fdb5b4e8c870e76fa3f244dac4b043dTimo Sirainen
ff3c528e094e75e3c6bc11af75ef84484d963ae9Timo Sirainenvoid dbox_file_free(struct dbox_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(file->refcount == 0);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->metadata_pool != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_unref(&file->metadata_pool);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_close(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(file->primary_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(file->alt_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid dbox_file_unref(struct dbox_file **_file)
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct dbox_file *file = *_file;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen *_file = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(file->refcount > 0);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (--file->refcount == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->storage->v.file_unrefed(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int dbox_file_parse_header(struct dbox_file *file, const char *line)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *const *tmp, *value;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int pos;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen enum dbox_header_key key;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->file_version = *line - '0';
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!i_isdigit(line[0]) || line[1] != ' ' ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (file->file_version != 1 && file->file_version != DBOX_VERSION)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_corrupted(file, "Invalid dbox version");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen line += 2;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pos = 2;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->msg_header_size = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen key = **tmp;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen value = *tmp + 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen switch (key) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case DBOX_HEADER_OLDV1_APPEND_OFFSET:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case DBOX_HEADER_MSG_HEADER_SIZE:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->msg_header_size = strtoul(value, NULL, 16);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case DBOX_HEADER_CREATE_STAMP:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->create_time = strtoul(value, NULL, 16);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pos += strlen(value) + 2;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->msg_header_size == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_corrupted(file, "Missing message header size");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int dbox_file_read_header(struct dbox_file *file)
3a9eb305fd4aad5502cb7e64625874385ab5bc19Timo Sirainen{
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen const char *line;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen unsigned int hdr_size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_seek(file->input, 0);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen line = i_stream_read_next_line(file->input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (line == NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->input->stream_errno == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_corrupted(file,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "EOF while reading file header");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_syscall_error(file, "read()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen hdr_size = file->input->v_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen T_BEGIN {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } T_END;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret > 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->file_header_size = hdr_size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool alt = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* try the primary path first */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = file->primary_path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen while ((file->fd = open(path, O_RDWR)) == -1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (errno != ENOENT) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(&file->storage->storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "open(%s) failed: %m", path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->alt_path == NULL || alt || !try_altpath) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* not found */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen }
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* try the alternative path */
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen path = file->alt_path;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen alt = TRUE;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->cur_path = path;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool *notfound_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *notfound_r = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->input != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->fd == -1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen T_BEGIN {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = dbox_file_open_fd(file, try_altpath);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } T_END;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret <= 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *notfound_r = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->input = i_stream_create_fd(file->fd, 0, FALSE);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_set_name(file->input, file->cur_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return dbox_file_read_header(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_open(struct dbox_file *file, bool *deleted_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return dbox_file_open_full(file, TRUE, deleted_r);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return dbox_file_open_full(file, FALSE, notfound_r);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_stat(struct dbox_file *file, struct stat *st_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool alt = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (dbox_file_is_open(file)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (fstat(file->fd, st_r) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(&file->storage->storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "fstat(%s) failed: %m", file->cur_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* try the primary path first */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = file->primary_path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen while (stat(path, st_r) < 0) {
63110c906fdb5b4e8c870e76fa3f244dac4b043dTimo Sirainen if (errno != ENOENT) {
63110c906fdb5b4e8c870e76fa3f244dac4b043dTimo Sirainen mail_storage_set_critical(&file->storage->storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "stat(%s) failed: %m", path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->alt_path == NULL || alt) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* not found */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* try the alternative path */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = file->alt_path;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen alt = TRUE;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen }
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen file->cur_path = path;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen return 0;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen}
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainenint dbox_file_header_write(struct dbox_file *file, struct ostream *output)
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen{
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen string_t *hdr;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen hdr = t_str_new(128);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen DBOX_HEADER_MSG_HEADER_SIZE,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (unsigned int)sizeof(struct dbox_message_header),
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->file_version = DBOX_VERSION;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->file_header_size = str_len(hdr);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->msg_header_size = sizeof(struct dbox_message_header);
e1dd1ec5a9d6c857697e7c72844822452bf1e1a0Timo Sirainen return o_stream_send(output, str_data(hdr), str_len(hdr));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid dbox_file_close(struct dbox_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_unlock(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->input != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_unref(&file->input);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->fd != -1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (close(file->fd) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_syscall_error(file, "close()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->fd = -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->cur_offset = (uoff_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_try_lock(struct dbox_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(file->fd != -1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen FILE_LOCK_METHOD_FCNTL, &file->lock);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(&file->storage->storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "file_try_lock(%s) failed: %m", file->cur_path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
e1dd1ec5a9d6c857697e7c72844822452bf1e1a0Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid dbox_file_unlock(struct dbox_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(!file->appending || file->lock == NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->lock != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file_unlock(&file->lock);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (file->input != NULL)
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen i_stream_sync(file->input);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen}
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct dbox_message_header hdr;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const unsigned char *data;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size_t size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = i_stream_read_data(file->input, &data, &size,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->msg_header_size - 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret <= 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->input->stream_errno == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* EOF, broken offset or file truncated */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_corrupted(file, "EOF reading msg header "
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen "(got %"PRIuSIZE_T"/%u bytes)",
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen size, file->msg_header_size);
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return 0;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen }
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen dbox_file_set_syscall_error(file, "read()");
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return -1;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen }
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen memcpy(&hdr, data, I_MIN(sizeof(hdr), file->msg_header_size));
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen /* probably broken offset */
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen dbox_file_set_corrupted(file, "msg header has bad magic value");
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return 0;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen }
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (data[file->msg_header_size-1] != '\n') {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen dbox_file_set_corrupted(file, "msg header doesn't end with LF");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *physical_size_r = hex2dec(hdr.message_size_hex,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sizeof(hdr.message_size_hex));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainenint dbox_file_get_mail_stream(struct dbox_file *file, uoff_t offset,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen struct istream **stream_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen uoff_t size;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen int ret;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen i_assert(file->input != NULL);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen if (offset == 0)
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen offset = file->file_header_size;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (offset != file->cur_offset) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen i_stream_seek(file->input, offset);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen ret = dbox_file_read_mail_header(file, &size);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (ret <= 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen file->cur_offset = offset;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen file->cur_physical_size = size;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen i_stream_seek(file->input, offset + file->msg_header_size);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (stream_r != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *stream_r = i_stream_create_limit(file->input,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen file->cur_physical_size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainendbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *line;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_stream_seek(file->input, *offset);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* skip over the actual metadata */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen while ((line = i_stream_read_next_line(file->input)) != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* end of metadata */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *offset = file->input->v_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainenvoid dbox_file_seek_rewind(struct dbox_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->cur_offset = (uoff_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uoff_t offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(file->input != NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->cur_offset == (uoff_t)-1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* first mail. we may not have read the file at all yet,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen so set the offset afterwards. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen offset = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen offset = file->cur_offset + file->msg_header_size +
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->cur_physical_size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *offset_r = file->cur_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *offset_r = offset;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (i_stream_is_eof(file->input)) {
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen *last_r = TRUE;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen return 0;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen }
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen *last_r = FALSE;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen ret = dbox_file_get_mail_stream(file, offset, NULL);
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (*offset_r == 0)
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen *offset_r = file->file_header_size;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen return ret;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen}
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainenstruct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen{
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen struct dbox_file_append_context *ctx;
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen i_assert(!file->appending);
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen file->appending = TRUE;
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen ctx = i_new(struct dbox_file_append_context, 1);
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen ctx->file = file;
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen if (file->fd != -1) {
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen o_stream_cork(ctx->output);
dd71e3d8d6284a4f80ddf010ee4316f688169b58Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ctx;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_append_commit(struct dbox_file_append_context **_ctx)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct dbox_file_append_context *ctx = *_ctx;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(ctx->file->appending);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *_ctx = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = dbox_file_append_flush(ctx);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen o_stream_unref(&ctx->output);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ctx->file->appending = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(ctx);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct dbox_file_append_context *ctx = *_ctx;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct dbox_file *file = ctx->file;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool close_file = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(ctx->file->appending);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *_ctx = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ctx->first_append_offset == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* nothing changed */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (ctx->first_append_offset == file->file_header_size) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* rollbacking everything */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (unlink(file->cur_path) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_syscall_error(file, "unlink()");
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen close_file = TRUE;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen } else {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen /* truncating only some mails */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen o_stream_close(ctx->output);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ftruncate(file->fd, ctx->first_append_offset) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_syscall_error(file, "ftruncate()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ctx->output != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen o_stream_unref(&ctx->output);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(ctx);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (close_file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_close(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->appending = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_append_flush(struct dbox_file_append_context *ctx)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ctx->last_flush_offset == ctx->output->offset)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (o_stream_flush(ctx->output) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dbox_file_set_syscall_error(ctx->file, "write()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (!ctx->file->storage->storage.set->fsync_disable) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (fdatasync(ctx->file->fd) < 0) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen dbox_file_set_syscall_error(ctx->file, "fdatasync()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ctx->last_flush_offset = ctx->output->offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct ostream **output_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct dbox_file *file = ctx->file;
f1ab91e28a4dac1305b52f77bb5f32269137d051Timo Sirainen struct stat st;
f1ab91e28a4dac1305b52f77bb5f32269137d051Timo Sirainen
f1ab91e28a4dac1305b52f77bb5f32269137d051Timo Sirainen if (ctx->output == NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* file creation had failed */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->file_version == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* 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;
}
o_stream_seek(ctx->output, st.st_size);
}
*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_data(file->input, &data, &size,
sizeof(metadata_hdr) - 1);
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;
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;
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);
}
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;
}
int dbox_file_move(struct dbox_file *file, bool alt_path)
{
struct ostream *output;
const char *dest_dir, *temp_path, *dest_path, *p;
struct stat st;
bool deleted;
int out_fd, ret = 0;
i_assert(file->input != NULL);
i_assert(file->lock != NULL);
if (dbox_file_is_in_alt(file) == alt_path)
return 0;
if (stat(file->cur_path, &st) < 0 && errno == ENOENT) {
/* already expunged/moved by another session */
dbox_file_unlock(file);
return 0;
}
dest_path = !alt_path ? file->primary_path : file->alt_path;
p = strrchr(dest_path, '/');
i_assert(p != NULL);
dest_dir = t_strdup_until(dest_path, p);
temp_path = t_strdup_printf("%s/%s", dest_dir,
dbox_generate_tmp_filename());
/* first copy the file. make sure to catch every possible error
since we really don't want to break the file. */
out_fd = file->storage->v.file_create_fd(file, temp_path, TRUE);
if (out_fd == -1)
return -1;
output = o_stream_create_fd_file(out_fd, 0, FALSE);
i_stream_seek(file->input, 0);
while ((ret = o_stream_send_istream(output, file->input)) > 0) ;
if (ret == 0)
ret = o_stream_flush(output);
if (output->stream_errno != 0) {
errno = output->stream_errno;
mail_storage_set_critical(&file->storage->storage,
"write(%s) failed: %m", temp_path);
ret = -1;
} else if (file->input->stream_errno != 0) {
errno = file->input->stream_errno;
dbox_file_set_syscall_error(file, "ftruncate()");
ret = -1;
} else if (ret < 0) {
mail_storage_set_critical(&file->storage->storage,
"o_stream_send_istream(%s, %s) "
"failed with unknown error",
temp_path, file->cur_path);
}
o_stream_unref(&output);
if (!file->storage->storage.set->fsync_disable && ret == 0) {
if (fsync(out_fd) < 0) {
mail_storage_set_critical(&file->storage->storage,
"fsync(%s) failed: %m", temp_path);
ret = -1;
}
}
if (close(out_fd) < 0) {
mail_storage_set_critical(&file->storage->storage,
"close(%s) failed: %m", temp_path);
ret = -1;
}
if (ret < 0) {
(void)unlink(temp_path);
return -1;
}
/* the temp file was successfully written. rename it now to the
destination file. the destination shouldn't exist, but if it does
its contents should be the same (except for maybe older metadata) */
if (rename(temp_path, dest_path) < 0) {
mail_storage_set_critical(&file->storage->storage,
"rename(%s, %s) failed: %m", temp_path, dest_path);
(void)unlink(temp_path);
return -1;
}
if (!file->storage->storage.set->fsync_disable) {
if (fdatasync_path(dest_dir) < 0) {
mail_storage_set_critical(&file->storage->storage,
"fdatasync(%s) failed: %m", dest_dir);
(void)unlink(dest_path);
return -1;
}
}
if (unlink(file->cur_path) < 0) {
dbox_file_set_syscall_error(file, "unlink()");
if (errno == EACCES) {
/* configuration problem? revert the write */
(void)unlink(dest_path);
}
/* who knows what happened to the file. keep both just to be
sure both won't get deleted. */
return -1;
}
/* file was successfully moved - reopen it */
dbox_file_close(file);
if (dbox_file_open(file, &deleted) <= 0) {
mail_storage_set_critical(&file->storage->storage,
"dbox_file_move(%s): reopening file failed", dest_path);
return -1;
}
return 0;
}
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 */
i_warning("dbox: File unexpectedly lost: %s",
file->primary_path);
return 0;
}
/* try the alternative path */
path = file->alt_path;
alt = TRUE;
}
return 1;
}