dbox-file.c revision b8d314c6355009ad0b9e332b6acecdfac5cc8891
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen static unsigned int create_count = 0;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return t_strdup_printf("temp.%lu.P%sQ%uM%u.%s",
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenvoid dbox_file_set_syscall_error(struct dbox_file *file, const char *function)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mail_storage_set_critical(&file->storage->storage,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen "%s failed for file %s: %m",
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenvoid dbox_file_set_corrupted(struct dbox_file *file, const char *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 Sirainenstatic int dbox_file_parse_header(struct dbox_file *file, const char *line)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen unsigned int pos;
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen (file->file_version != 1 && file->file_version != DBOX_VERSION)) {
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen dbox_file_set_corrupted(file, "Invalid dbox version");
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen for (tmp = t_strsplit(line, " "); *tmp != NULL; tmp++) {
ce89e2964b6bc4925d2dd690417200a110d041c5Timo Sirainen file->msg_header_size = strtoul(value, NULL, 16);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen dbox_file_set_corrupted(file, "Missing message header size");
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic int dbox_file_read_header(struct dbox_file *file)
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen "EOF while reading file header");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstatic int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen /* try the primary path first */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainen while ((file->fd = open(path, O_RDWR)) == -1) {
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen mail_storage_set_critical(&file->storage->storage,
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen if (file->alt_path == NULL || alt || !try_altpath) {
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen /* not found */
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen /* try the alternative path */
b321df9603081896b70ec44635af96d674a9839aTimo Sirainenstatic int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
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 Sirainenint dbox_file_open(struct dbox_file *file, bool *deleted_r)
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen return dbox_file_open_full(file, TRUE, deleted_r);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainenint dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen return dbox_file_open_full(file, FALSE, notfound_r);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainenint dbox_file_stat(struct dbox_file *file, struct stat *st_r)
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen mail_storage_set_critical(&file->storage->storage,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* try the primary path first */
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen mail_storage_set_critical(&file->storage->storage,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen /* not found */
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen /* try the alternative path */
70c181da837ed85fc5b0426c010b65609bda5329Timo Sirainenint dbox_file_header_write(struct dbox_file *file, struct ostream *output)
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen str_printfa(hdr, "%u %c%x %c%x\n", DBOX_VERSION,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen (unsigned int)sizeof(struct dbox_message_header),
e2a700d0628e395d64cbcef4b5b4510816bf51c4Timo Sirainen DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen file->msg_header_size = sizeof(struct dbox_message_header);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen return o_stream_send(output, str_data(hdr), str_len(hdr));
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen mail_storage_set_critical(&file->storage->storage,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen "file_try_lock(%s) failed: %m", file->cur_path);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen i_assert(!file->appending || file->lock == NULL);
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainenint dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen const unsigned char *data;
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen ret = i_stream_read_data(file->input, &data, &size,
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen /* EOF, broken offset or file truncated */
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen dbox_file_set_corrupted(file, "EOF reading msg header "
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");
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen dbox_file_set_corrupted(file, "msg header doesn't end with LF");
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen *physical_size_r = hex2dec(hdr.message_size_hex,
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainenint dbox_file_get_mail_stream(struct dbox_file *file, uoff_t offset,
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen ret = dbox_file_read_mail_header(file, &size);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_stream_seek(file->input, offset + file->msg_header_size);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen *stream_r = i_stream_create_limit(file->input,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainendbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
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 */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid dbox_file_seek_rewind(struct dbox_file *file)
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainenint dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* first mail. we may not have read the file at all yet,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen so set the offset afterwards. */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen offset = file->cur_offset + file->msg_header_size +
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if ((ret = dbox_file_seek_next_at_metadata(file, &offset)) <= 0) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ret = dbox_file_get_mail_stream(file, offset, NULL);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenstruct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ctx = i_new(struct dbox_file_append_context, 1);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainenint dbox_file_append_commit(struct dbox_file_append_context **_ctx)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenvoid dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* nothing changed */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen } else if (ctx->first_append_offset == file->file_header_size) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* rollbacking everything */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen dbox_file_set_syscall_error(file, "unlink()");
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen /* truncating only some mails */
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen if (ftruncate(file->fd, ctx->first_append_offset) < 0)
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen dbox_file_set_syscall_error(file, "ftruncate()");
2526d52441ef368215ab6bf04fd0356d3b09d235Timo Sirainenint dbox_file_append_flush(struct dbox_file_append_context *ctx)
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen if (ctx->last_flush_offset == ctx->output->offset)
c1ebcdad1b4d950eb22219704dd9d64a89d0568fTimo Sirainen dbox_file_set_syscall_error(ctx->file, "write()");
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (!ctx->file->storage->storage.set->fsync_disable) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen dbox_file_set_syscall_error(ctx->file, "fdatasync()");
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenint dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* file creation had failed */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* newly created file, write the file header */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (dbox_file_header_write(file, ctx->output) < 0) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* file has existing mails */
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen file->msg_header_size != sizeof(struct dbox_message_header)) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* created by an incompatible version, can't append */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* first append to existing file. seek to eof first. */
const unsigned char *data;
int ret;
if (ret <= 0) {
const char *line;
int ret;
return ret;
ret = 0;
if (ret == 0)
return ret;
int ret;
if (ret <= 0)
return ret;
const char *const *metadata;
unsigned int i, count;
for (i = 0; i < count; i++) {
return NULL;
bool deleted;
if (ret == 0)
} else if (ret < 0) {
if (ret < 0) {
const char *path;