function, file->current_path);
}
-static void dbox_file_set_corrupted(struct dbox_file *file, const char *reason)
+void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
{
+ va_list args;
+
+ va_start(args, reason);
mail_storage_set_critical(&file->storage->storage,
"Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
file->current_path,
- file->input == NULL ? 0 : file->input->v_offset, reason);
+ file->input == NULL ? 0 : file->input->v_offset,
+ t_strdup_vprintf(reason, args));
+ va_end(args);
}
static struct dbox_file *
if (ret <= 0) {
if (file->input->stream_errno == 0) {
/* EOF, broken offset or file truncated */
- dbox_file_set_corrupted(file, t_strdup_printf(
- "EOF reading msg header "
- "(got %"PRIuSIZE_T"/%u bytes)",
- size, file->msg_header_size));
+ dbox_file_set_corrupted(file, "EOF reading msg header "
+ "(got %"PRIuSIZE_T"/%u bytes)",
+ size, file->msg_header_size);
return 0;
}
dbox_file_set_syscall_error(file, "read()");
}
static int
-dbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset,
- uoff_t *physical_size_r)
+dbox_file_seek_next_at_metadata(struct dbox_file *file, uoff_t *offset)
{
const char *line;
+ uoff_t physical_size;
int ret;
if ((ret = dbox_file_metadata_skip_header(file)) <= 0)
*offset = file->input->v_offset;
(void)i_stream_read(file->input);
- if (!i_stream_have_bytes_left(file->input)) {
- *physical_size_r = 0;
+ if (!i_stream_have_bytes_left(file->input))
return 1;
- }
- return dbox_file_read_mail_header(file, physical_size_r);
+ return dbox_file_read_mail_header(file, &physical_size);
}
-int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset,
- uoff_t *physical_size_r, bool *last_r)
+int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset, bool *last_r)
{
uoff_t size;
bool first = *offset == 0;
if (deleted) {
*last_r = TRUE;
- return 1;
+ return 0;
}
- if (first) {
- *physical_size_r = size;
+ if (first)
return 1;
- }
i_stream_skip(file->input, size);
- return dbox_file_seek_next_at_metadata(file, offset, physical_size_r);
+ return dbox_file_seek_next_at_metadata(file, offset);
}
static int
#include "dbox-map.h"
#include "dbox-sync.h"
+#include <stdlib.h>
+
struct dbox_mail_move {
struct dbox_file *file;
uint32_t offset;
return 1;
}
+static int dbox_map_file_msg_offset_cmp(const void *p1, const void *p2)
+{
+ const struct dbox_map_file_msg *m1 = p1, *m2 = p2;
+
+ if (m1->offset < m2->offset)
+ return -1;
+ else if (m1->offset > m2->offset)
+ return 1;
+ else
+ return 0;
+}
+
+static int
+dbox_sync_file_copy_metadata(struct dbox_file *file, struct ostream *output)
+{
+ struct dbox_metadata_header meta_hdr;
+ const char *line;
+ const unsigned char *data;
+ size_t size;
+ int ret;
+
+ ret = i_stream_read_data(file->input, &data, &size,
+ sizeof(meta_hdr));
+ if (ret <= 0) {
+ i_assert(ret == -1);
+ if (file->input->stream_errno == 0) {
+ dbox_file_set_corrupted(file, "missing metadata");
+ return 0;
+ }
+ // FIXME
+ return -1;
+ }
+
+ memcpy(&meta_hdr, data, sizeof(meta_hdr));
+ if (memcmp(meta_hdr.magic_post, DBOX_MAGIC_POST,
+ sizeof(meta_hdr.magic_post)) != 0) {
+ dbox_file_set_corrupted(file, "invalid metadata magic");
+ return 0;
+ }
+ i_stream_skip(file->input, sizeof(meta_hdr));
+ if (output != NULL)
+ o_stream_send(output, &meta_hdr, sizeof(meta_hdr));
+ while ((line = i_stream_read_next_line(file->input)) != NULL) {
+ if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+ /* end of metadata */
+ break;
+ }
+ if (output != NULL) {
+ o_stream_send_str(output, line);
+ o_stream_send(output, "\n", 1);
+ }
+ }
+ if (line == NULL) {
+ dbox_file_set_corrupted(file, "missing end-of-metadata line");
+ return 0;
+ }
+ if (output != NULL)
+ o_stream_send(output, "\n", 1);
+ return 1;
+}
+
int dbox_sync_file_cleanup(struct dbox_file *file)
{
struct dbox_file *out_file;
struct stat st;
struct istream *input;
struct ostream *output = NULL;
- struct dbox_metadata_header meta_hdr;
struct dbox_map_append_context *append_ctx;
ARRAY_TYPE(dbox_map_file_msg) msgs_arr;
- const struct dbox_map_file_msg *msgs;
+ struct dbox_map_file_msg *msgs;
ARRAY_TYPE(seq_range) copied_map_uids, expunged_map_uids;
unsigned int i, count;
- uoff_t physical_size, msg_size;
- const unsigned char *data;
- size_t size;
- const char *line;
+ uoff_t offset, physical_size, msg_size;
bool expunged;
int ret;
array_free(&msgs_arr);
return -1;
}
- msgs = array_get(&msgs_arr, &count);
+ msgs = array_get_modifiable(&msgs_arr, &count);
+ /* sort messages by their offset */
+ qsort(msgs, count, sizeof(*msgs), dbox_map_file_msg_offset_cmp);
+
i_array_init(&copied_map_uids, I_MIN(count, 1));
i_array_init(&expunged_map_uids, I_MIN(count, 1));
+ offset = file->file_header_size;
for (i = 0; i < count; i++) {
- if (msgs[i].refcount == 0) {
- seq_range_array_add(&expunged_map_uids, 0,
- msgs[i].map_uid);
- continue;
- }
- ret = dbox_file_get_mail_stream(file, msgs[i].offset,
- &physical_size,
- NULL, &expunged);
- if (ret <= 0) {
- /* FIXME: handle corruption? */
- ret = -1;
+ if ((ret = dbox_file_get_mail_stream(file, offset,
+ &physical_size,
+ NULL, &expunged)) <= 0)
break;
- }
+ msg_size = file->msg_header_size + physical_size;
- /* non-expunged message. write it to output file. */
- if (dbox_map_append_next(append_ctx, physical_size,
- &out_file, &output) < 0) {
- // FIXME
- ret = -1;
+ if (msgs[i].offset != offset) {
+ /* map doesn't match file's actual contents */
+ dbox_file_set_corrupted(file,
+ "cleanup found mismatched offsets "
+ "(%"PRIuUOFF_T" vs %u, %u/%u)",
+ offset, msgs[i].offset, i, count);
+ ret = 0;
break;
}
- i_assert(file->file_id != out_file->file_id);
- i_stream_seek(file->input, msgs[i].offset);
- msg_size = file->msg_header_size + physical_size;
- input = i_stream_create_limit(file->input, msg_size);
- ret = o_stream_send_istream(output, input);
- i_stream_unref(&input);
- if (ret != (off_t)(file->msg_header_size + physical_size)) {
- // FIXME
- ret = -1;
- break;
- }
+ if (msgs[i].refcount == 0) {
+ seq_range_array_add(&expunged_map_uids, 0,
+ msgs[i].map_uid);
+ output = NULL;
+ } else {
+ /* non-expunged message. write it to output file. */
+ if (dbox_map_append_next(append_ctx, physical_size,
+ &out_file, &output) < 0) {
+ ret = -1;
+ break;
+ }
+ i_assert(file->file_id != out_file->file_id);
- /* copy metadata */
- i_stream_seek(file->input, msgs[i].offset + msg_size);
- ret = i_stream_read_data(file->input, &data, &size,
- sizeof(meta_hdr));
- if (ret <= 0) {
- // FIXME
- i_assert(ret != -2);
- ret = -1;
- break;
- }
- memcpy(&meta_hdr, data, sizeof(meta_hdr));
- if (memcmp(meta_hdr.magic_post, DBOX_MAGIC_POST,
- sizeof(meta_hdr.magic_post)) != 0) {
- // FIXME
- ret = -1;
- break;
- }
- i_stream_skip(file->input, sizeof(meta_hdr));
- o_stream_send(output, &meta_hdr, sizeof(meta_hdr));
- while ((line = i_stream_read_next_line(file->input)) != NULL) {
- if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
- /* end of metadata */
+ i_stream_seek(file->input, offset);
+ input = i_stream_create_limit(file->input, msg_size);
+ ret = o_stream_send_istream(output, input);
+ i_stream_unref(&input);
+ if (ret != (off_t)msg_size) {
+ // FIXME
+ ret = -1;
break;
}
- o_stream_send_str(output, line);
- o_stream_send(output, "\n", 1);
}
- if (line == NULL) {
- // FIXME
- ret = -1;
+
+ /* copy/skip metadata */
+ i_stream_seek(file->input, offset + msg_size);
+ if ((ret = dbox_sync_file_copy_metadata(file, output)) <= 0)
break;
- }
- o_stream_send(output, "\n", 1);
+
dbox_map_append_finish_multi_mail(append_ctx);
seq_range_array_add(&copied_map_uids, 0, msgs[i].map_uid);
+ offset = file->input->v_offset;
+ }
+ if (offset != (uoff_t)st.st_size && ret > 0) {
+ /* file has more messages than what map tells us */
+ dbox_file_set_corrupted(file,
+ "more messages available than in map "
+ "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size);
+ ret = 0;
+ sleep(3600);
}
array_free(&msgs_arr); msgs = NULL;
- if (ret < 0) {
+ if (ret == 0) {
+ // FIXME: ..?
+ i_error("dbox corrupted");
+ dbox_map_append_rollback(&append_ctx);
+ ret = -1;
+ } else if (ret < 0) {
+ i_error("dbox error");
dbox_map_append_rollback(&append_ctx);
ret = -1;
} else if (output == NULL) {