Mercurial > dovecot > original-hg > dovecot-1.2
changeset 3148:d9327a3dbf97 HEAD
forgot to add
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 09 Feb 2005 00:50:26 +0200 |
parents | 6c7d463eaf1e |
children | d0981562a9a3 |
files | src/lib-index/mail-transaction-log-append.c |
diffstat | 1 files changed, 451 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-index/mail-transaction-log-append.c Wed Feb 09 00:50:26 2005 +0200 @@ -0,0 +1,451 @@ +/* Copyright (C) 2003-2004 Timo Sirainen */ + +#include "lib.h" +#include "ioloop.h" +#include "buffer.h" +#include "write-full.h" +#include "mail-index-private.h" +#include "mail-index-view-private.h" +#include "mail-index-transaction-private.h" +#include "mail-transaction-log-private.h" + +static int log_append_buffer(struct mail_transaction_log_file *file, + const buffer_t *buf, const buffer_t *hdr_buf, + enum mail_transaction_type type, int external) +{ + struct mail_transaction_header hdr; + const void *data, *hdr_data; + size_t size, hdr_data_size; + uint32_t hdr_size; + + i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0); + + data = buffer_get_data(buf, &size); + if (size == 0) + return 0; + + i_assert((size % 4) == 0); + + if (hdr_buf != NULL) { + hdr_data = buffer_get_data(hdr_buf, &hdr_data_size); + i_assert((hdr_data_size % 4) == 0); + } else { + hdr_data = NULL; + hdr_data_size = 0; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.type = type; + if (type == MAIL_TRANSACTION_EXPUNGE) + hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT; + if (external) + hdr.type |= MAIL_TRANSACTION_EXTERNAL; + + hdr_size = + mail_index_uint32_to_offset(sizeof(hdr) + size + hdr_data_size); + if (file->first_append_size == 0) { + /* size will be written later once everything is in disk */ + file->first_append_size = hdr_size; + } else { + hdr.size = hdr_size; + } + + if (pwrite_full(file->fd, &hdr, sizeof(hdr), file->sync_offset) < 0) + return -1; + file->sync_offset += sizeof(hdr); + + if (hdr_data_size > 0) { + if (pwrite_full(file->fd, hdr_data, hdr_data_size, + file->sync_offset) < 0) + return -1; + file->sync_offset += hdr_data_size; + } + + if (pwrite_full(file->fd, data, size, file->sync_offset) < 0) + return -1; + file->sync_offset += size; + return 0; +} + +static const buffer_t * +log_get_hdr_update_buffer(struct mail_index_transaction *t) +{ + buffer_t *buf; + struct mail_transaction_header_update u; + uint16_t offset; + int state = 0; + + memset(&u, 0, sizeof(u)); + + buf = buffer_create_dynamic(pool_datastack_create(), 256); + for (offset = 0; offset <= sizeof(t->hdr_change); offset++) { + if (offset < sizeof(t->hdr_change) && t->hdr_mask[offset]) { + if (state == 0) { + u.offset = offset; + state++; + } + } else { + if (state > 0) { + u.size = offset - u.offset; + buffer_append(buf, &u, sizeof(uint16_t)*2); + buffer_append(buf, t->hdr_change + u.offset, + u.size); + state = 0; + } + } + } + return buf; +} + +static int log_append_ext_intro(struct mail_transaction_log_file *file, + struct mail_index_transaction *t, + uint32_t ext_id, uint32_t reset_id) +{ + const struct mail_index_ext *ext; + struct mail_transaction_ext_intro *intro; + buffer_t *buf; + uint32_t idx; + size_t size; + + if (!mail_index_map_get_ext_idx(t->view->map, ext_id, &idx)) { + /* new extension */ + idx = (uint32_t)-1; + } + + ext = t->view->index->extensions->data; + ext += ext_id; + + if (t->ext_resizes == NULL) { + intro = NULL; + size = 0; + } else { + intro = buffer_get_modifyable_data(t->ext_resizes, &size); + size /= sizeof(*intro); + } + + buf = buffer_create_dynamic(pool_datastack_create(), 128); + if (ext_id < size && intro[ext_id].name_size != 0) { + /* we're resizing it */ + intro += ext_id; + + i_assert(intro->ext_id == idx); + intro->name_size = idx != (uint32_t)-1 ? 0 : + strlen(ext->name); + buffer_append(buf, intro, sizeof(*intro)); + } else { + /* generate a new intro structure */ + intro = buffer_append_space_unsafe(buf, sizeof(*intro)); + intro->ext_id = idx; + intro->hdr_size = ext->hdr_size; + intro->record_size = ext->record_size; + intro->record_align = ext->record_align; + intro->name_size = idx != (uint32_t)-1 ? 0 : + strlen(ext->name); + } + if (reset_id != 0) { + /* we're going to reset this extension in this transaction */ + intro->reset_id = reset_id; + } else if (idx != (uint32_t)-1) { + /* use the existing reset_id */ + const struct mail_index_ext *map_ext = + t->view->map->extensions->data; + map_ext += idx; + + intro->reset_id = map_ext->reset_id; + } else { + /* new extension, reset_id defaults to 0 */ + } + buffer_append(buf, ext->name, intro->name_size); + + if ((buf->used % 4) != 0) + buffer_append_zero(buf, 4 - (buf->used % 4)); + + return log_append_buffer(file, buf, NULL, MAIL_TRANSACTION_EXT_INTRO, + t->external); +} + +static int +mail_transaction_log_append_ext_intros(struct mail_transaction_log_file *file, + struct mail_index_transaction *t) +{ + const struct mail_transaction_ext_intro *resize; + struct mail_transaction_ext_reset ext_reset; + uint32_t ext_id, ext_count, update_count, resize_count, reset_count; + const uint32_t *reset; + const buffer_t *const *update; + buffer_t *buf; + size_t size; + + if (t->ext_rec_updates == NULL) { + update = NULL; + update_count = 0; + } else { + update = buffer_get_data(t->ext_rec_updates, &size); + update_count = size / sizeof(*update); + } + + if (t->ext_resizes == NULL) { + resize = NULL; + resize_count = 0; + } else { + resize = buffer_get_data(t->ext_resizes, &size); + resize_count = size / sizeof(*resize); + } + + if (t->ext_resets == NULL) { + reset = NULL; + reset_count = 0; + } else { + reset = buffer_get_data(t->ext_resets, &size); + reset_count = size / sizeof(*reset); + } + + memset(&ext_reset, 0, sizeof(ext_reset)); + + buf = buffer_create_data(pool_datastack_create(), + &ext_reset, sizeof(ext_reset)); + buffer_set_used_size(buf, sizeof(ext_reset)); + ext_count = I_MAX(I_MAX(update_count, resize_count), reset_count); + + for (ext_id = 0; ext_id < ext_count; ext_id++) { + ext_reset.new_reset_id = + ext_id < reset_count && reset[ext_id] != 0 ? + reset[ext_id] : 0; + if ((ext_id < resize_count && resize[ext_id].name_size) || + (ext_id < update_count && update[ext_id] != NULL) || + ext_reset.new_reset_id != 0) { + if (log_append_ext_intro(file, t, ext_id, 0) < 0) + return -1; + } + if (ext_reset.new_reset_id != 0) { + if (log_append_buffer(file, buf, NULL, + MAIL_TRANSACTION_EXT_RESET, + t->external) < 0) + return -1; + } + } + + return 0; +} + +static int log_append_ext_rec_updates(struct mail_transaction_log_file *file, + struct mail_index_transaction *t) +{ + buffer_t **updates; + const uint32_t *reset; + uint32_t ext_id, reset_id, reset_count; + size_t size; + + if (t->ext_rec_updates == NULL) { + updates = NULL; + size = 0; + } else { + updates = buffer_get_modifyable_data(t->ext_rec_updates, &size); + size /= sizeof(*updates); + } + + if (t->ext_resets == NULL) { + reset = NULL; + reset_count = 0; + } else { + reset = buffer_get_data(t->ext_resets, &size); + reset_count = size / sizeof(*reset); + } + + for (ext_id = 0; ext_id < size; ext_id++) { + if (updates[ext_id] == NULL) + continue; + + reset_id = ext_id < reset_count && reset[ext_id] != 0 ? + reset[ext_id] : 0; + if (log_append_ext_intro(file, t, ext_id, reset_id) < 0) + return -1; + + if (log_append_buffer(file, updates[ext_id], NULL, + MAIL_TRANSACTION_EXT_REC_UPDATE, + t->external) < 0) + return -1; + } + return 0; +} + +static int log_append_keyword_updates(struct mail_transaction_log_file *file, + struct mail_index_transaction *t) +{ + struct mail_index *index = t->view->index; + struct mail_transaction_keyword_update kt_hdr; + buffer_t *hdr_buf, **buf; + size_t i, size; + + hdr_buf = buffer_create_dynamic(pool_datastack_create(), 64); + + buf = buffer_get_modifyable_data(t->keyword_updates, &size); + size /= sizeof(*buf); + for (i = 0; i < size; i++) { + if (buf[i] == NULL) + continue; + + buffer_set_used_size(hdr_buf, 0); + + memset(&kt_hdr, 0, sizeof(kt_hdr)); + kt_hdr.modify_type = (i & 1) == 0 ? MODIFY_ADD : MODIFY_REMOVE; + kt_hdr.name_size = strlen(index->keywords[i / 2]); + buffer_append(hdr_buf, &kt_hdr, sizeof(kt_hdr)); + buffer_append(hdr_buf, index->keywords[i / 2], + kt_hdr.name_size); + if ((hdr_buf->used % 4) != 0) + buffer_append_zero(hdr_buf, 4 - (hdr_buf->used % 4)); + + if (log_append_buffer(file, buf[i], hdr_buf, + MAIL_TRANSACTION_KEYWORD_UPDATE, + t->external) < 0) + return -1; + } + + return 0; +} + +int mail_transaction_log_append(struct mail_index_transaction *t, + uint32_t *log_file_seq_r, + uoff_t *log_file_offset_r) +{ + struct mail_index_view *view = t->view; + struct mail_index *index; + struct mail_transaction_log *log; + struct mail_transaction_log_file *file; + struct mail_index_header idx_hdr; + uoff_t append_offset; + unsigned int lock_id; + int ret; + + index = mail_index_view_get_index(view); + log = index->log; + + if (!t->log_updates) { + /* nothing to append */ + *log_file_seq_r = 0; + *log_file_offset_r = 0; + return 0; + } + + if (log->index->log_locked) { + i_assert(t->external); + } else { + if (mail_transaction_log_lock_head(log) < 0) + return -1; + + /* update sync_offset */ + if (mail_transaction_log_file_map(log->head, + log->head->sync_offset, + (uoff_t)-1) < 0) { + mail_transaction_log_file_unlock(log->head); + return -1; + } + } + + if (log->head->sync_offset > MAIL_TRANSACTION_LOG_ROTATE_SIZE && + log->head->last_mtime < + ioloop_time - MAIL_TRANSACTION_LOG_ROTATE_MIN_TIME) { + /* we might want to rotate, but check first that everything is + synced in index. */ + if (mail_index_lock_shared(log->index, TRUE, &lock_id) < 0) { + if (!log->index->log_locked) + mail_transaction_log_file_unlock(log->head); + return -1; + } + idx_hdr = *log->index->hdr; + mail_index_unlock(log->index, lock_id); + + if (log->head->hdr.file_seq == idx_hdr.log_file_seq && + log->head->sync_offset == idx_hdr.log_file_int_offset && + log->head->sync_offset == idx_hdr.log_file_ext_offset) { + if (mail_transaction_log_rotate(log, TRUE) < 0) { + /* that didn't work. well, try to continue + anyway */ + } + } + } + + file = log->head; + file->first_append_size = 0; + append_offset = file->sync_offset; + + ret = 0; + + /* send all extension introductions and resizes before appends + to avoid resize overhead as much as possible */ + ret = mail_transaction_log_append_ext_intros(file, t); + + if (t->appends != NULL && ret == 0) { + ret = log_append_buffer(file, t->appends, NULL, + MAIL_TRANSACTION_APPEND, t->external); + } + if (t->updates != NULL && ret == 0) { + ret = log_append_buffer(file, t->updates, NULL, + MAIL_TRANSACTION_FLAG_UPDATE, + t->external); + } + + if (t->ext_rec_updates != NULL && ret == 0) + ret = log_append_ext_rec_updates(file, t); + + /* keyword resets before updates */ + if (t->keyword_resets != NULL && ret == 0) { + ret = log_append_buffer(file, t->keyword_resets, NULL, + MAIL_TRANSACTION_KEYWORD_RESET, + t->external); + } + if (t->keyword_updates != NULL && ret == 0) + ret = log_append_keyword_updates(file, t); + + if (t->expunges != NULL && ret == 0) { + ret = log_append_buffer(file, t->expunges, NULL, + MAIL_TRANSACTION_EXPUNGE, t->external); + } + if (t->hdr_changed && ret == 0) { + ret = log_append_buffer(file, log_get_hdr_update_buffer(t), + NULL, MAIL_TRANSACTION_HEADER_UPDATE, + t->external); + } + + if (ret < 0) { + mail_index_file_set_syscall_error(log->index, file->filepath, + "pwrite()"); + } + + if (ret == 0 && (t->updates != NULL || t->appends != NULL) && + t->hide_transaction) { + mail_index_view_add_synced_transaction(view, file->hdr.file_seq, + append_offset); + } + + if (ret == 0 && fsync(file->fd) < 0) { + /* we don't know how much of it got written, + it may be corrupted now.. */ + mail_index_file_set_syscall_error(log->index, file->filepath, + "fsync()"); + ret = -1; + } + + if (ret == 0 && file->first_append_size != 0) { + /* synced - rewrite first record's header */ + ret = pwrite_full(file->fd, &file->first_append_size, + sizeof(uint32_t), append_offset); + if (ret < 0) { + mail_index_file_set_syscall_error(log->index, + file->filepath, + "pwrite()"); + } + } + + if (ret < 0) + file->sync_offset = append_offset; + + *log_file_seq_r = file->hdr.file_seq; + *log_file_offset_r = file->sync_offset; + + if (!log->index->log_locked) + mail_transaction_log_file_unlock(file); + return ret; +} +