dbox-file.c revision b8d314c6355009ad0b9e332b6acecdfac5cc8891
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "lib.h"
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen#include "ioloop.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "array.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "hex-dec.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "hex-binary.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "hostpid.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "istream.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "ostream.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "file-lock.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "mkdir-parents.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "fdatasync-path.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "eacces-error.h"
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen#include "str.h"
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen#include "dbox-storage.h"
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen#include "dbox-file.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <stdio.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <stdlib.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <unistd.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <ctype.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <fcntl.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#define DBOX_READ_BLOCK_SIZE 4096
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenconst char *dbox_generate_tmp_filename(void)
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen{
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen static unsigned int create_count = 0;
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return t_strdup_printf("temp.%lu.P%sQ%uM%u.%s",
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen (unsigned long)ioloop_timeval.tv_sec, my_pid,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen create_count++,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen (unsigned int)ioloop_timeval.tv_usec,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen my_hostname);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenvoid dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mail_storage_set_critical(&file->storage->storage,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen "%s failed for file %s: %m",
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen function, file->cur_path);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenvoid dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen va_list args;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->storage->files_corrupted = TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen va_start(args, reason);
9398c0935613ba038cf2275ff66c43b25092cfd0Timo Sirainen mail_storage_set_critical(&file->storage->storage,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen "Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->cur_path, file->input == NULL ? 0 : file->input->v_offset,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen t_strdup_vprintf(reason, args));
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen va_end(args);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainenvoid dbox_file_init(struct dbox_file *file)
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen{
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen file->refcount = 1;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen file->fd = -1;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen file->cur_offset = (uoff_t)-1;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen file->cur_path = file->primary_path;
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen}
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainenvoid dbox_file_free(struct dbox_file *file)
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(file->refcount == 0);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (file->metadata_pool != NULL)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen pool_unref(&file->metadata_pool);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen dbox_file_close(file);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen i_free(file->primary_path);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen i_free(file->alt_path);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen i_free(file);
4376643cd2c7110e752c09f838f2c4eee6ed8ac6Timo Sirainen}
4376643cd2c7110e752c09f838f2c4eee6ed8ac6Timo Sirainen
4376643cd2c7110e752c09f838f2c4eee6ed8ac6Timo Sirainenvoid dbox_file_unref(struct dbox_file **_file)
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen{
99aec80ae156689abd5a568c647884d0c72f143cTimo Sirainen struct dbox_file *file = *_file;
99aec80ae156689abd5a568c647884d0c72f143cTimo Sirainen
99aec80ae156689abd5a568c647884d0c72f143cTimo Sirainen *_file = NULL;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen i_assert(file->refcount > 0);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen if (--file->refcount == 0)
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen file->storage->v.file_unrefed(file);
4376643cd2c7110e752c09f838f2c4eee6ed8ac6Timo Sirainen}
4376643cd2c7110e752c09f838f2c4eee6ed8ac6Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic int dbox_file_parse_header(struct dbox_file *file, const char *line)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const char *const *tmp, *value;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen unsigned int pos;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen enum dbox_header_key key;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->file_version = *line - '0';
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen if (!i_isdigit(line[0]) || line[1] != ' ' ||
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen (file->file_version != 1 && file->file_version != DBOX_VERSION)) {
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen dbox_file_set_corrupted(file, "Invalid dbox version");
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen return -1;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen line += 2;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen pos = 2;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen
fc8d5f0ac909cca77840538e8beef98a8d40c21cTimo Sirainen file->msg_header_size = 0;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) {
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen key = **tmp;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen value = *tmp + 1;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen switch (key) {
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen case DBOX_HEADER_OLDV1_APPEND_OFFSET:
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen break;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen case DBOX_HEADER_MSG_HEADER_SIZE:
ce89e2964b6bc4925d2dd690417200a110d041c5Timo Sirainen file->msg_header_size = strtoul(value, NULL, 16);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen break;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen case DBOX_HEADER_CREATE_STAMP:
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen file->create_time = strtoul(value, NULL, 16);
fc8d5f0ac909cca77840538e8beef98a8d40c21cTimo Sirainen break;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen }
fc8d5f0ac909cca77840538e8beef98a8d40c21cTimo Sirainen pos += strlen(value) + 2;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen }
fc8d5f0ac909cca77840538e8beef98a8d40c21cTimo Sirainen
5d4855d7b4dcffb6975ed8e3c9c376dac74e5c8aTimo Sirainen if (file->msg_header_size == 0) {
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen dbox_file_set_corrupted(file, "Missing message header size");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen return 0;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen}
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic int dbox_file_read_header(struct dbox_file *file)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const char *line;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen unsigned int hdr_size;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen int ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_stream_seek(file->input, 0);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen line = i_stream_read_next_line(file->input);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (line == NULL) {
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen if (file->input->stream_errno == 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen dbox_file_set_corrupted(file,
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen "EOF while reading file header");
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return 0;
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen dbox_file_set_syscall_error(file, "read()");
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return -1;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen hdr_size = file->input->v_offset;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen T_BEGIN {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } T_END;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (ret > 0)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->file_header_size = hdr_size;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen const char *path;
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen bool alt = FALSE;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen /* try the primary path first */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen path = file->primary_path;
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen while ((file->fd = open(path, O_RDWR)) == -1) {
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen if (errno != ENOENT) {
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen mail_storage_set_critical(&file->storage->storage,
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen "open(%s) failed: %m", path);
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen return -1;
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen }
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen if (file->alt_path == NULL || alt || !try_altpath) {
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen /* not found */
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen return 0;
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen }
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen /* try the alternative path */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen path = file->alt_path;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen alt = TRUE;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen file->cur_path = path;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return 1;
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen}
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen
b321df9603081896b70ec44635af96d674a9839aTimo Sirainenstatic int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen bool *notfound_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen int ret;
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen *notfound_r = FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (file->input != NULL)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen if (file->fd == -1) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen T_BEGIN {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ret = dbox_file_open_fd(file, try_altpath);
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen } T_END;
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen if (ret <= 0) {
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen if (ret < 0)
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen return -1;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen *notfound_r = TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 1;
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen }
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen }
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen file->input = i_stream_create_fd(file->fd, 0, FALSE);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen i_stream_set_name(file->input, file->cur_path);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen return dbox_file_read_header(file);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen}
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainenint dbox_file_open(struct dbox_file *file, bool *deleted_r)
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen{
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen return dbox_file_open_full(file, TRUE, deleted_r);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen}
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainenint dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen{
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen return dbox_file_open_full(file, FALSE, notfound_r);
4376643cd2c7110e752c09f838f2c4eee6ed8ac6Timo Sirainen}
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainenint dbox_file_stat(struct dbox_file *file, struct stat *st_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen const char *path;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen bool alt = FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen if (dbox_file_is_open(file)) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (fstat(file->fd, st_r) < 0) {
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen mail_storage_set_critical(&file->storage->storage,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen "fstat(%s) failed: %m", file->cur_path);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* try the primary path first */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen path = file->primary_path;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen while (stat(path, st_r) < 0) {
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen if (errno != ENOENT) {
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen mail_storage_set_critical(&file->storage->storage,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen "stat(%s) failed: %m", path);
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen return -1;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen if (file->alt_path == NULL || alt) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* not found */
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return -1;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen /* try the alternative path */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen path = file->alt_path;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen alt = TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->cur_path = path;
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen return 0;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen}
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainenint dbox_file_header_write(struct dbox_file *file, struct ostream *output)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen{
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen string_t *hdr;
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen hdr = t_str_new(128);
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen DBOX_HEADER_MSG_HEADER_SIZE,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen (unsigned int)sizeof(struct dbox_message_header),
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen file->file_version = DBOX_VERSION;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen file->file_header_size = str_len(hdr);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen file->msg_header_size = sizeof(struct dbox_message_header);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return o_stream_send(output, str_data(hdr), str_len(hdr));
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainen}
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainen
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainenvoid dbox_file_close(struct dbox_file *file)
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainen{
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainen dbox_file_unlock(file);
43358fffb1d9f3091fd94895e0ac4643c50e2388Timo Sirainen if (file->input != NULL)
25ee72451d16374ed27fdbf829f4ec756c778352Timo Sirainen i_stream_unref(&file->input);
a3dd97fb6d92a89c3de0597fed2d4b044c7aeb84Timo Sirainen if (file->fd != -1) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (close(file->fd) < 0)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen dbox_file_set_syscall_error(file, "close()");
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen file->fd = -1;
484e12acec34f16e5a8adc001e23ae48f1dda8c7Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen file->cur_offset = (uoff_t)-1;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint dbox_file_try_lock(struct dbox_file *file)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen int ret;
484e12acec34f16e5a8adc001e23ae48f1dda8c7Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(file->fd != -1);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen FILE_LOCK_METHOD_FCNTL, &file->lock);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (ret < 0) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen mail_storage_set_critical(&file->storage->storage,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen "file_try_lock(%s) failed: %m", file->cur_path);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return ret;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenvoid dbox_file_unlock(struct dbox_file *file)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(!file->appending || file->lock == NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (file->lock != NULL)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen file_unlock(&file->lock);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (file->input != NULL)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_stream_sync(file->input);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen{
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen struct dbox_message_header hdr;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen const unsigned char *data;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen size_t size;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen int ret;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen ret = i_stream_read_data(file->input, &data, &size,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen file->msg_header_size - 1);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (ret <= 0) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (file->input->stream_errno == 0) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen /* EOF, broken offset or file truncated */
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen dbox_file_set_corrupted(file, "EOF reading msg header "
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen "(got %"PRIuSIZE_T"/%u bytes)",
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen size, file->msg_header_size);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return 0;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen dbox_file_set_syscall_error(file, "read()");
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return -1;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen memcpy(&hdr, data, I_MIN(sizeof(hdr), file->msg_header_size));
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (memcmp(hdr.magic_pre, DBOX_MAGIC_PRE, sizeof(hdr.magic_pre)) != 0) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen /* probably broken offset */
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen dbox_file_set_corrupted(file, "msg header has bad magic value");
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen return 0;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (data[file->msg_header_size-1] != '\n') {
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen dbox_file_set_corrupted(file, "msg header doesn't end with LF");
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen *physical_size_r = hex2dec(hdr.message_size_hex,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen sizeof(hdr.message_size_hex));
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 1;
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen}
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainenint dbox_file_get_mail_stream(struct dbox_file *file, uoff_t offset,
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen struct istream **stream_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen uoff_t size;
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen int ret;
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen i_assert(file->input != NULL);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen if (offset == 0)
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen offset = file->file_header_size;
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen if (offset != file->cur_offset) {
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen i_stream_seek(file->input, offset);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen ret = dbox_file_read_mail_header(file, &size);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen if (ret <= 0)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->cur_offset = offset;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->cur_physical_size = size;
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_stream_seek(file->input, offset + file->msg_header_size);
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen if (stream_r != NULL) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen *stream_r = i_stream_create_limit(file->input,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->cur_physical_size);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic int
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainendbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen const char *line;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen int ret;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_stream_seek(file->input, *offset);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* skip over the actual metadata */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen while ((line = i_stream_read_next_line(file->input)) != NULL) {
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* end of metadata */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen break;
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen }
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen }
bd354c19cb93c07ade79477674328a54146ea332Timo Sirainen *offset = file->input->v_offset;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return 1;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen}
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid dbox_file_seek_rewind(struct dbox_file *file)
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen{
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen file->cur_offset = (uoff_t)-1;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen}
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainenint dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen uoff_t offset;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen int ret;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(file->input != NULL);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (file->cur_offset == (uoff_t)-1) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* first mail. we may not have read the file at all yet,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen so set the offset afterwards. */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen offset = 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen offset = file->cur_offset + file->msg_header_size +
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->cur_physical_size;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *offset_r = file->cur_offset;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *offset_r = offset;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen if (i_stream_is_eof(file->input)) {
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen *last_r = TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *last_r = FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ret = dbox_file_get_mail_stream(file, offset, NULL);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (*offset_r == 0)
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen *offset_r = file->file_header_size;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstruct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct dbox_file_append_context *ctx;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(!file->appending);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen file->appending = TRUE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ctx = i_new(struct dbox_file_append_context, 1);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ctx->file = file;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (file->fd != -1) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen o_stream_cork(ctx->output);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return ctx;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainenint dbox_file_append_commit(struct dbox_file_append_context **_ctx)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct dbox_file_append_context *ctx = *_ctx;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen int ret;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen i_assert(ctx->file->appending);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *_ctx = NULL;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ret = dbox_file_append_flush(ctx);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen o_stream_unref(&ctx->output);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ctx->file->appending = FALSE;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_free(ctx);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenvoid dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen struct dbox_file_append_context *ctx = *_ctx;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct dbox_file *file = ctx->file;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen bool close_file = FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(ctx->file->appending);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen *_ctx = NULL;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (ctx->first_append_offset == 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* nothing changed */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else if (ctx->first_append_offset == file->file_header_size) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* rollbacking everything */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (unlink(file->cur_path) < 0)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen dbox_file_set_syscall_error(file, "unlink()");
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen close_file = TRUE;
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen } else {
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen /* truncating only some mails */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen o_stream_close(ctx->output);
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen if (ftruncate(file->fd, ctx->first_append_offset) < 0)
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen dbox_file_set_syscall_error(file, "ftruncate()");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (ctx->output != NULL)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen o_stream_unref(&ctx->output);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_free(ctx);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (close_file)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen dbox_file_close(file);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen file->appending = FALSE;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2526d52441ef368215ab6bf04fd0356d3b09d235Timo Sirainenint dbox_file_append_flush(struct dbox_file_append_context *ctx)
2526d52441ef368215ab6bf04fd0356d3b09d235Timo Sirainen{
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen if (ctx->last_flush_offset == ctx->output->offset)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (o_stream_flush(ctx->output) < 0) {
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen dbox_file_set_syscall_error(ctx->file, "write()");
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen return -1;
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (!ctx->file->storage->storage.set->fsync_disable) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (fdatasync(ctx->file->fd) < 0) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen dbox_file_set_syscall_error(ctx->file, "fdatasync()");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen ctx->last_flush_offset = ctx->output->offset;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenint dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen struct ostream **output_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct dbox_file *file = ctx->file;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen struct stat st;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (ctx->output == NULL) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* file creation had failed */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (file->file_version == 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* newly created file, write the file header */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (dbox_file_header_write(file, ctx->output) < 0) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen dbox_file_set_syscall_error(file, "write()");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return -1;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen *output_r = ctx->output;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen return 1;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* file has existing mails */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (file->file_version != DBOX_VERSION ||
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen file->msg_header_size != sizeof(struct dbox_message_header)) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* created by an incompatible version, can't append */
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen return 0;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (ctx->output->offset == 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* 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;
}