#include "write-full.h"
#include "mail-index-private.h"
#include "mail-index-view-private.h"
+#include "mail-index-modseq.h"
#include "mail-index-transaction-private.h"
#include "mail-transaction-log-private.h"
struct mail_index_transaction *trans;
buffer_t *output;
- unsigned int modseq_change_count;
+ uint64_t modseq;
uint32_t first_append_size;
bool sync_includes_this;
};
{
struct mail_transaction_header hdr;
uint32_t hdr_size;
+ size_t hdr_pos;
i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0);
i_assert((buf->used % 4) == 0);
hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT;
if ((ctx->trans->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0)
hdr.type |= MAIL_TRANSACTION_EXTERNAL;
+ hdr.size = sizeof(hdr) + buf->used +
+ (hdr_buf == NULL ? 0 : hdr_buf->used);
- if (mail_transaction_header_has_modseq(&hdr))
- ctx->modseq_change_count++;
+ hdr_pos = ctx->output->used;
+ buffer_append(ctx->output, &hdr, sizeof(hdr));
+ if (hdr_buf != NULL)
+ buffer_append(ctx->output, hdr_buf->data, hdr_buf->used);
+ buffer_append(ctx->output, buf->data, buf->used);
- hdr_size = mail_index_uint32_to_offset(sizeof(hdr) + buf->used +
- (hdr_buf == NULL ? 0 :
- hdr_buf->used));
+ if (mail_transaction_header_has_modseq(buf->data,
+ CONST_PTR_OFFSET(buf->data, sizeof(hdr)), ctx->modseq))
+ ctx->modseq++;
+
+ /* update the size */
+ hdr_size = mail_index_uint32_to_offset(hdr.size);
if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(ctx->file) &&
ctx->first_append_size == 0) {
/* size will be written later once everything
is in disk */
ctx->first_append_size = hdr_size;
+ hdr.size = 0;
} else {
hdr.size = hdr_size;
}
-
- buffer_append(ctx->output, &hdr, sizeof(hdr));
- if (hdr_buf != NULL)
- buffer_append(ctx->output, hdr_buf->data, hdr_buf->used);
- buffer_append(ctx->output, buf->data, buf->used);
+ buffer_write(ctx->output, hdr_pos, &hdr, sizeof(hdr));
}
static int log_buffer_move_to_memory(struct log_append_context *ctx)
/* new extension, reset_id defaults to 0 */
}
buffer_append(buf, rext->name, intro->name_size);
-
if ((buf->used % 4) != 0)
buffer_append_zero(buf, 4 - (buf->used % 4));
+ if (ctx->file->sync_highest_modseq == 0 &&
+ strcmp(rext->name, MAIL_INDEX_MODSEQ_EXT_NAME) == 0) {
+ /* modseq tracking started */
+ ctx->file->sync_highest_modseq = 1;
+ }
+
log_append_buffer(ctx, buf, NULL, MAIL_TRANSACTION_EXT_INTRO);
}
ctx.file = file;
ctx.trans = t;
ctx.output = buffer_create_dynamic(default_pool, 1024);
+ ctx.modseq = file->sync_highest_modseq;
/* send all extension introductions and resizes before appends
to avoid resize overhead as much as possible */
buffer_free(&ctx.output);
return -1;
}
- file->sync_highest_modseq += ctx.modseq_change_count;
+ file->sync_highest_modseq = ctx.modseq;
buffer_free(&ctx.output);
if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_HIDE) != 0) {
}
static void
-mail_transaction_log_file_add_to_list(struct mail_transaction_log_file *file)
+mail_transaction_log_file_skip_to_head(struct mail_transaction_log_file *file)
{
struct mail_transaction_log *log = file->log;
- struct mail_transaction_log_file **p;
struct mail_index_map *map = log->index->map;
const struct mail_index_modseq_header *modseq_hdr;
+ uoff_t head_offset;
- if (map != NULL && file->hdr.file_seq == map->hdr.log_file_seq &&
- map->hdr.log_file_head_offset != 0) {
- /* we can get a valid log offset from index file. initialize
- sync_offset from it so we don't have to read the whole log
- file from beginning. */
- uoff_t head_offset = map->hdr.log_file_head_offset;
-
- modseq_hdr = mail_index_map_get_modseq_header(map);
- if (head_offset < file->hdr.hdr_size) {
- mail_index_set_error(log->index,
- "%s: log_file_head_offset too small",
- log->index->filepath);
- file->sync_offset = file->hdr.hdr_size;
- file->sync_highest_modseq = file->hdr.initial_modseq;
- } else if (modseq_hdr == NULL ||
- modseq_hdr->log_seq != file->hdr.file_seq ||
- modseq_hdr->log_offset != head_offset) {
- /* highest_modseq not synced, start from beginning */
- file->sync_offset = file->hdr.hdr_size;
- file->sync_highest_modseq = file->hdr.initial_modseq;
- } else {
- file->sync_offset = head_offset;
- file->sync_highest_modseq = modseq_hdr->highest_modseq;
- }
- file->saved_tail_offset = map->hdr.log_file_tail_offset;
- } else {
+ if (map == NULL || file->hdr.file_seq != map->hdr.log_file_seq ||
+ map->hdr.log_file_head_offset == 0)
+ return;
+
+ /* we can get a valid log offset from index file. initialize
+ sync_offset from it so we don't have to read the whole log
+ file from beginning. */
+ head_offset = map->hdr.log_file_head_offset;
+
+ modseq_hdr = mail_index_map_get_modseq_header(map);
+ if (head_offset < file->hdr.hdr_size) {
+ mail_index_set_error(log->index,
+ "%s: log_file_head_offset too small",
+ log->index->filepath);
file->sync_offset = file->hdr.hdr_size;
file->sync_highest_modseq = file->hdr.initial_modseq;
+ } else if (modseq_hdr == NULL && file->hdr.initial_modseq == 0) {
+ /* modseqs not used yet */
+ file->sync_offset = head_offset;
+ file->sync_highest_modseq = 0;
+ } else if (modseq_hdr->log_seq != file->hdr.file_seq) {
+ /* highest_modseq not synced, start from beginning */
+ file->sync_offset = file->hdr.hdr_size;
+ file->sync_highest_modseq = file->hdr.initial_modseq;
+ } else if (modseq_hdr->log_offset > head_offset) {
+ mail_index_set_error(log->index,
+ "%s: modseq_hdr.log_offset too large",
+ log->index->filepath);
+ file->sync_offset = file->hdr.hdr_size;
+ file->sync_highest_modseq = file->hdr.initial_modseq;
+ } else {
+ /* start from where we last stopped tracking modseqs */
+ file->sync_offset = modseq_hdr->log_offset;
+ file->sync_highest_modseq = modseq_hdr->highest_modseq;
}
+ file->saved_tail_offset = log->index->map->hdr.log_file_tail_offset;
+}
+
+static void
+mail_transaction_log_file_add_to_list(struct mail_transaction_log_file *file)
+{
+ struct mail_transaction_log_file **p;
+
+ file->sync_offset = file->hdr.hdr_size;
+ file->sync_highest_modseq = file->hdr.initial_modseq;
+ mail_transaction_log_file_skip_to_head(file);
/* insert it to correct position */
- for (p = &log->files; *p != NULL; p = &(*p)->next) {
+ for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
if ((*p)->hdr.file_seq > file->hdr.file_seq)
break;
i_assert((*p)->hdr.file_seq < file->hdr.file_seq);
hdr->prev_file_seq = index->map->hdr.log_file_seq;
hdr->prev_file_offset = index->map->hdr.log_file_head_offset;
hdr->file_seq = index->map->hdr.log_file_seq + 1;
- hdr->initial_modseq =
+ hdr->initial_modseq = log->head == NULL ||
+ log->head->sync_highest_modseq == 0 ? 0 :
mail_index_map_modseq_get_highest(index->map);
} else {
hdr->file_seq = 1;
shouldn't have filled */
memset(PTR_OFFSET(&file->hdr, file->hdr.hdr_size), 0,
sizeof(file->hdr) - file->hdr.hdr_size);
- if (file->hdr.minor_version == 0)
- file->hdr.initial_modseq = 1;
}
if (file->hdr.indexid == 0) {
if (reset) {
file->hdr.prev_file_seq = 0;
file->hdr.prev_file_offset = 0;
- file->hdr.initial_modseq = 1;
+ file->hdr.initial_modseq = 0;
}
if (write_full(new_fd, &file->hdr, sizeof(file->hdr)) < 0) {
}
bool
-mail_transaction_header_has_modseq(const struct mail_transaction_header *hdr)
+mail_transaction_header_has_modseq(const struct mail_transaction_header *hdr,
+ const void *data,
+ uint64_t cur_modseq)
{
+ if (cur_modseq != 0) {
+ /* tracking modseqs */
+ } else if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) ==
+ MAIL_TRANSACTION_EXT_INTRO) {
+ /* modseqs not tracked yet. see if this is a modseq
+ extension introduction. */
+ const struct mail_transaction_ext_intro *intro = data;
+ const unsigned int modseq_ext_len =
+ strlen(MAIL_INDEX_MODSEQ_EXT_NAME);
+
+ if (intro->name_size == modseq_ext_len &&
+ memcmp(intro + 1, MAIL_INDEX_MODSEQ_EXT_NAME,
+ modseq_ext_len) == 0) {
+ /* modseq tracking started */
+ return TRUE;
+ }
+ } else {
+ /* not tracking modseqs */
+ return FALSE;
+ }
+
switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
case MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT:
if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
while (cur_offset < offset) {
if (log_get_synced_record(file, &cur_offset, &hdr) < 0)
return- 1;
- if (mail_transaction_header_has_modseq(hdr))
+ if (mail_transaction_header_has_modseq(hdr, hdr + 1,
+ cur_modseq))
cur_modseq++;
}
prev_offset = cur_offset;
if (log_get_synced_record(file, &cur_offset, &hdr) < 0)
return -1;
- if (mail_transaction_header_has_modseq(hdr)) {
+ if (mail_transaction_header_has_modseq(hdr, hdr + 1,
+ cur_modseq)) {
if (++cur_modseq == modseq)
break;
}
const struct mail_transaction_header *hdr,
unsigned int trans_size)
{
+ const void *data = hdr + 1;
int ret;
- if (mail_transaction_header_has_modseq(hdr))
+ if (mail_transaction_header_has_modseq(hdr, hdr + 1,
+ file->sync_highest_modseq))
file->sync_highest_modseq++;
-
if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0)
return 0;
if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) ==
MAIL_TRANSACTION_HEADER_UPDATE) {
/* see if this updates mailbox_sync_offset */
- ret = log_file_track_mailbox_sync_offset_hdr(file, hdr + 1,
+ ret = log_file_track_mailbox_sync_offset_hdr(file, data,
trans_size -
sizeof(*hdr));
if (ret != 0)
}
}
- if (start_offset > file->sync_offset) {
- /* although we could just skip over the unwanted data, we have
- to sync everything so that modseqs are calculated
- correctly */
- start_offset = file->sync_offset;
- }
-
if (file->buffer != NULL && file->buffer_offset > start_offset) {
/* we have to insert missing data to beginning of buffer */
ret = mail_transaction_log_file_insert_read(file, start_offset);
end_offset);
}
+ if (start_offset > file->sync_offset)
+ mail_transaction_log_file_skip_to_head(file);
+ if (start_offset > file->sync_offset) {
+ /* although we could just skip over the unwanted data, we have
+ to sync everything so that modseqs are calculated
+ correctly */
+ start_offset = file->sync_offset;
+ }
+
if (!index->mmap_disable)
ret = mail_transaction_log_file_map_mmap(file, start_offset);
else {
printf("create stamp = %u\n", hdr.create_stamp);
printf("initial modseq = %llu\n",
(unsigned long long)hdr.initial_modseq);
- *modseq_r = I_MAX(hdr.initial_modseq, 1);
+ *modseq_r = hdr.initial_modseq;
}
static bool
}
static void log_record_print(const struct mail_transaction_header *hdr,
- const void *data)
+ const void *data, uint64_t *modseq)
{
unsigned int size = hdr->size - sizeof(*hdr);
printf(" - flags = %u\n", intro->flags);
printf(" - name_size = %u\n", intro->name_size);
if (intro->name_size > 0) {
- printf(" - name = '%.*s'\n",
- intro->name_size, (const char *)(intro+1));
+ const char *name = (const char *)(intro+1);
+
+ printf(" - name = '%.*s'\n", intro->name_size, name);
+ if (*modseq == 0 && intro->name_size == 6 &&
+ memcmp(name, "modseq", 6) == 0)
+ *modseq = 1;
}
break;
}
printf("record: offset=%"PRIuUOFF_T", type=%s, size=%u",
offset, log_record_type(hdr.type), hdr.size);
- if (mail_transaction_header_has_modseq(&hdr)) {
+ if (*modseq > 0 && mail_transaction_header_has_modseq(&hdr)) {
*modseq += 1;
printf(", modseq=%llu", (unsigned long long)*modseq);
}
i_fatal("rec data read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
ret, hdr.size - sizeof(hdr));
}
- log_record_print(&hdr, buf);
+ log_record_print(&hdr, buf, modseq);
} else {
lseek(fd, hdr.size - sizeof(hdr), SEEK_CUR);
}